pax_global_header 0000666 0000000 0000000 00000000064 14174530510 0014512 g ustar 00root root 0000000 0000000 52 comment=9a9d0eafd14a3c8b5c03516cb8a9c6199ac0d4b9
bolt-0.9.2/ 0000775 0000000 0000000 00000000000 14174530510 0012462 5 ustar 00root root 0000000 0000000 bolt-0.9.2/.devcontainer/ 0000775 0000000 0000000 00000000000 14174530510 0015221 5 ustar 00root root 0000000 0000000 bolt-0.9.2/.devcontainer/Dockerfile 0000664 0000000 0000000 00000001554 14174530510 0017220 0 ustar 00root root 0000000 0000000 FROM fedora:latest
RUN dnf install -y \
asciidoc \
bat \
clang-analyzer \
ccls \
codespell \
findutils \
fish \
fpaste \
gcc \
git \
glib2-devel \
glibc-langpack-en \
gtk-doc \
hostname \
ipython3 \
jq \
lcov \
libgudev-devel \
lsof \
make \
meson \
packit \
polkit-devel \
pylint \
python3 \
python3-autopep8 \
python3-dbus \
python3-dbusmock \
python3-devel \
python3-docutils \
python3-enchant \
python3-gobject \
python3-jsonschema \
python3-pip \
python3-pycodestyle \
python3-pylint \
python3-pytest \
python3-pyyaml \
redhat-rpm-config \
rpm-build \
rpmlint \
skopeo \
strace \
systemd-devel \
the_silver_searcher \
tree \
umockdev-devel \
uncrustify
WORKDIR /workspaces/bolt
bolt-0.9.2/.devcontainer/devcontainer.json 0000664 0000000 0000000 00000001761 14174530510 0020602 0 ustar 00root root 0000000 0000000 {
"name": "bolt",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
},
"mounts": [
"source=bold-share,target=/root/.local/share,type=volume"
],
"runArgs": [
"--privileged"
],
"settings": {
"C_Cpp.autocomplete": "Disabled",
"C_Cpp.formatting": "Disabled",
"C_Cpp.errorSquiggles": "Disabled",
"C_Cpp.intelliSenseEngine": "Disabled",
"terminal.integrated.shell.linux": "/bin/fish",
"python.pythonPath": "/usr/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.nosetestsEnabled": false,
"python.testing.pyTestEnabled": true,
"python.testing.pyTestArgs": [
"test"
]
},
"extensions": [
"asabil.meson",
"editorconfig.editorconfig",
"ccls-project.ccls",
"laurenttreguier.rpm-spec",
"ms-python.python",
"ms-python.vscode-pylance",
"ms-vscode.cpptools",
"ms-vscode-remote.remote-containers",
]
}
bolt-0.9.2/.editorconfig 0000664 0000000 0000000 00000000427 14174530510 0015142 0 ustar 00root root 0000000 0000000 root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.{c,h}]
indent_style = space
indent_size = 2
indent_brace_style = gnu
[meson.build]
indent_style = space
indent_size = 2
[.md]
indent_style = space
indent_size = 2
bolt-0.9.2/.flake8 0000664 0000000 0000000 00000000037 14174530510 0013635 0 ustar 00root root 0000000 0000000 [flake8]
max-line-length = 120
bolt-0.9.2/.gitignore 0000664 0000000 0000000 00000000317 14174530510 0014453 0 ustar 00root root 0000000 0000000
# files
*~
*.log
*.xz
# directories
build/
dist/
# coverity
cov-int/
coverity/
# IDEs and related
.ccls-cache/
.cquery_cached_index/
compile_commands.json
.idea
.gdb_history
.vscode/
# emacs
TAGS
\#*\#
bolt-0.9.2/.gitlab-ci.yml 0000664 0000000 0000000 00000002277 14174530510 0015126 0 ustar 00root root 0000000 0000000 image: docker:latest
services:
- docker:dind
.build-template: &build
script:
- docker build -t bolt-$OS -f ./contrib/Dockerfile-$OS .
- mkdir build-$OS
- docker run --cap-drop=dac_override -e -t -v `pwd`:/src -v `pwd`/build-$OS:/build bolt-$OS ./contrib/docker-build.sh
artifacts:
paths:
- build-$OS/meson-logs
expire_in: 1 week
fedora:
stage: build
variables:
OS: fedora
<<: *build
arch:
stage: build
allow_failure: true
variables:
OS: arch
<<: *build
debian:
stage: build
variables:
OS: debian
<<: *build
alpine:
stage: build
variables:
OS: alpine
<<: *build
coverity:
stage: build
only:
refs:
- schedules
variables:
- $COVERITY_TOKEN && $COVERITY_EMAIL
script:
- mkdir build-coverity
- docker build --build-arg ORG=gicmo --build-arg PROJECT=bolt --build-arg TOKEN=$COVERITY_TOKEN -t bolt-coverity -f ./contrib/Dockerfile-coverity .
- docker run --rm -e COVERITY_TOKEN=$COVERITY_TOKEN -e COVERITY_EMAIL=$COVERITY_EMAIL -t -v `pwd`:/src:Z -v `pwd`/build-coverity:/build:Z bolt-coverity ./contrib/coverity.sh
artifacts:
paths:
- build-coverity/cov-int/build-log.txt
expire_in: 1 week
bolt-0.9.2/.pylintrc 0000664 0000000 0000000 00000000250 14174530510 0014324 0 ustar 00root root 0000000 0000000 [MASTER]
disable=missing-docstring,invalid-name,duplicate-code,superfluous-parens,too-many-locals,attribute-defined-outside-init,too-many-arguments
max-line-length=120
bolt-0.9.2/.travis.yml 0000664 0000000 0000000 00000000573 14174530510 0014600 0 ustar 00root root 0000000 0000000 language: c
sudo: required
dist: bionic
services:
- docker
env:
- OS=alpine
- OS=debian
- OS=arch
- OS=fedora
install:
- docker build -t bolt-$OS -f ./contrib/Dockerfile-$OS .
script:
- mkdir build-$OS
- sudo chown $(id -u):root build-$OS
- docker run --cap-drop=dac_override -e -t -v `pwd`:/src -v `pwd`/build-$OS:/build bolt-$OS ./contrib/docker-build.sh
bolt-0.9.2/BUGS.md 0000664 0000000 0000000 00000000765 14174530510 0013554 0 ustar 00root root 0000000 0000000 Bugs
====
Bugs are tracked at [fd.o's gitlab][gitlab issues].
Debugging
=========
The daemon can be run in verbose mode with the command line option `-v`.
This should normally not be necessary, except when debugging the DBus
layer of boltd (the verbose output is indeed very verbose).
To replace the currently running daemon and run a new instance of it
in the foreground, launch the daemon with `--replace`:
boltd --replace
[gitlab issues]: https://gitlab.freedesktop.org/bolt/bolt/issues
bolt-0.9.2/CHANGELOG.md 0000664 0000000 0000000 00000022675 14174530510 0014307 0 ustar 00root root 0000000 0000000 Version 0.9.2
-------------
_Please get along_
Released: 2022-01-27
* This release is compatible with umockdev >= 0.16.3; there was a change in
umockdev that made our test fail with it, since both our test and umockev
were trying to create the same directorires. bolt now allows for the dir
to already exist.
* The license for `90-bolt.rules` has changed from `GPL-2.1+`, which does
not exist and was probably was confused with `LGPL-2.1+`, to `GPL 2.0+`.
* Documentation has been updated and spelling mistakes fixed.
* Various improvements for continuous integration.
* The minimum required version of meson has been bumped to 0.46.0.
Version 0.9.1
-------------
_Unstable icy waters_
Released: 2020-11-30
* Bug fixes for integrated thunderbolt controllers:
On Ice Lake, the Thunderbolt 3 i/o subsystem is fully integrated into the die.
As a side effect it does not have a DROM, which means the host udev device
does not have the device and vendor name and id attributes.
Additionally the `unique_id` of said host controller changes with every boot,
which breaks one of the fundamental assumptions in `boltd`. Therefore a number
of bug fixes were necessary to properly support this new architecture:
- Don't store domains where uuids change across reboots [!220]
- Fixes for the journal and the domain's acl-log [!221]
- Version the store and use that to clean up stale domains once [!226, !231]
- Host identification for embedded thunderbolt controllers [!233]
* Various other small bug fixes and memory leak fixes.
Version 0.9
-----------
_Four comes after Three_
Released: 2020-06-15
* New Features:
- Add 'Generation' attribute for the Manager [!197]
- Ability to change the policy of a stored device [!202]
- The BootACL Domain property is now writable [!184]
- Support for systemd's service watchdog [!185]
- Expose Link Speed sysfs attributes [!214]
* Improvements:
- boltclt: show timestamps in 'monitor' call [!208]
- Persist the host device [!194]
* Bug fixes:
- Fix a flaky test [!217, #161]
- Plug small memory leaks in error conditions [!217]
- Ignore spurious wakeup device uevents for probing [!209]
- Preserve keystate when updating devices [!192]
Version 0.8
-----------
_I owe it to the MM U!_
Released: 2019-06-14
* New Features:
- **IOMMU support**: adapt behavior iommu support is present and active [#128]
- automatically enroll new devices with the new `iommu` policy when iommu is active
- automatically authorize devices with the `iommu` policy if iommu is active
- `boltctl config` command to describe, get and set global, device and domain properties.
- Chain authorization and enrollment via `boltctl {enroll, authorize} --chain` [!153, !154]
- `bolt-mock` script for interactively testing `boltd` [!152]
* Improvements:
- Automatically import devices that were authorized at boot [#137]
- Make tests installable [#140]
- Honour `STATE_DIRECTORY` [!159] and `RUNTIME_DIRECTORY` [!161]
- Profiling support via gprof [!168]
* Bug fixes:
- Better handling of random data generation [#132, !165]
- Fix double free in case of client creation failure [!148]
- Fix invalid format string in warning [!14]
* NB for packagers:
- The dbus configuration is now installed in `$datadir/dbus-1/system.d` instead of `$sysconfdir` [!177].
- To install tests, configure with `-Dinstall-tests=true`.
Version 0.7
-----------
_The Known Unknowns_
Released: 2019-01-01
* Features:
- announce status to systemd via sd_notify (using a simple custom implementation) [!143]
* Bug fixes:
- properly update global security level status [#131 via !141]
- adapt to `systemd` 240 not sending `bind`/`unbind` uevents [#133 via !145]
- fix compilation on musl [#126 via !140]
- daemon: use `g_unix_signal_source…` to catch signals [#127, #129 via !138]
* Improvements
- precondition checks cleanup and completion [#124 via !139]
- error cleanup [#125, !142]
- fix some leaks and issues uncovered by coverity [!144]
Version 0.6
-----------
_Make the firmware do it!_
Released: 2018-11-28
* New Features:
- **pre-boot access control list, aka. `BootACL`** support [!119]
- domains objects are now persistent
- new `Uid` (dbus) / `uid` (object) property derived from the uuid of the device representing the root switch
- `sysfs` and `id` attribute will be set/unset on connects and disconnects
- domains are now stored in the boltd database
- domains got the `BootACL` (dbus) / `bootacl` (object) property
- uuids can be added, removed or set in batch
- when domain is *online*: changes are written to the sysfs `boot_acl` attribute directly
- when domain is *offline*: changes are written to a journal and then reapplied in order when the domain is connected
- newly enrolled devices get added to all bootacls of all domains *if* the `policy` is `BOLT_POLICY_AUTO`
- removed devices get deleted from all bootacls of all domains
- `boltacl domain` command will show the bootacl slots and their content
- `boltctl` gained the `-U, --uuid` option, to control how uuids are printed [!124]
* Improvements and fixes:
- Testing [!127]
- The test coverage increased to `84.80%` overall and to `90.0%` for the `boltd` source
- Coverage is reported for merge requests via the fedora ci image [!126]
- `boltctl` is now included in the tests [!132]
- Fedora 29 is used for the fedora ci image
- Bugs and robustness:
- The device state is verified in `Device.Authorize` [!120]
- Handle empty 'keys' sysfs device attribute [!129]
- Properly adjust policies when enrolling already authorized devices [!136]
- Fix potential crash when logging assertions `g_return_if_fail` [!121]
Version 0.5
-----------
_You've got the Power_
Released: 2018-09-28
* New Features:
- Force-Power DBus API ⚡(!101)
- A new interface to boltd to control the (force) power mechanism (#106)
- Switch off power with a delay so we don't run into races (#104)
- Add representation of thunderbolt domains
This is a preparation for the boot acl support
- Authorizing devices, after upgrading from `USER` to `SECURE` security level, will lead to key upgrades (!107)
- Connection and Authorization times are now stored (!105)
- Systemd dependency is now optional (!106, !103)
- Company and brand names are cleaned up for the display name (#102)
* Bug fixes and cleanups:
- Emit proper notification for security-level property changes (!100)
- Auto generate the object path for BoltDevice (!102)
* NB for packagers:
- `-Ddb-path` is **DEPRECATED**, use `-Ddb-name` instead (!113)
- meson >= 0.44.0 is required.
- systemd unit files got updated:
- `After=polkit.service` (!116)
- Use systemd for runtime and state directory management (!113)
- Sandbox is tightened (!97)
Version 0.4
-----------
_The Race Is Over_
Released: 2018-05-28
* New features:
- auto import of devices authorized during boot [!90]
- allow enrolling of already authorized devices, i.e. importing of devices [!86]
- label new devices and detect duplicates [!91]
* Be more robust:
- Handle NULL errors in logging code better [!89]
- Properly handle empty device database entries [!87]
- Better authentication errors and logging [!85]
- More tests
* Internal changes:
- Make sure we don't miss device status changes [!82]
- Rework property change notification dispatching [!83]
Version 0.3
-----------
_Capture The Flags_
Released: 2018-05-28
* Prepare for upcoming kernel changes:
- Support for `usbonly` (SL4) security level (#75)
- Support for `boot` sysfs device attribute (#76)
* DBus API changes:
- `BoltStatus` was split (#81), so that:
- `Device.Status` does not report `authorized-xxx` anymore
- `Device.AuthFlags` added to indicate auth details, e.g. `secure`, `nopci`, `boot`, `nokey` (#76)
- `BoltSecurity` and thus `Manager.SecurityLevel` can report `usbonly` (#75)
* client/boltctl:
- async versions for many function calls
- more efficient getters, resulting in reduced allocations
- boltctl reports `Device.AuthFlags`
- boltctl prints more and better version info via `boltctl monitor`
* Other bugfixes and improvements include:
- more robust flags/enum conversion
Version 0.2
-----------
_I broke the Bus_
Released: 2018-03-06
Lots of changes, the most significant:
- database location moved (now in `/var/lib/boltd`)
- **⚠** devices enrolled with bolt 0.1 need to be re-enrolled (or the database moved from the old location)
- DBus API changed (lots of strings)
- Enums are transmitted as strings
- `Device.Security` property is gone; replaced by `authorized-dponly` status and `Manager.SecurityLevel` ( #37, #38, #62)
- Various timestamps got added: `Device.ConnectTime`, `Device.StoreTime` and `Device.AuthorizeTime` (#46 #57)
- `Device.Label` (readwrite) was added so devices can be given custom names (#46)
- `Device.Type` added, to differentiate between host and peripherals
- `Manager.AuthMode` (readwrite) was added to control (auto) authorization (#48)
Other bugfixes and improvements include:
- Ensure we get a `DeviceAdded` signal on startup (#58)
- Support for legacy devices that have no key sysfs attribute (#67)
- Use structured logging and avoid printing UUIDs in non-debug log code (#36 #60)
- Other internal restructuring for cleaner code (#43)
Version 0.1
-----------
_Accidentally Working_
Released: 2017-12-13
* functional daemon that can authorize enroll and authorize devices
* `boltctl` command to interact with the daemon
bolt-0.9.2/COPYING 0000664 0000000 0000000 00000063642 14174530510 0013530 0 ustar 00root root 0000000 0000000 GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
bolt-0.9.2/HACKING.md 0000664 0000000 0000000 00000004672 14174530510 0014061 0 ustar 00root root 0000000 0000000 Patches
=======
Patches should be submitted in the form of merge requests at
[gitlab][gitlab].
Coding style
============
The style is codified via the supplied `sscripts/uncrustify.cfg` config.
It can be automatically formatted by the `uncrustify` target, i.e. by
invoking `ninja -C uncrustify`.
Make sure to format the source code before submitting pull requests.
Testing
=======
To run the test suite in verbose mode:
meson test -C build --verbose
To run `boltd` from within valgrind for the integration tests set the
environment variable `VALGRIND`. A [suppression file][valgrind] can be
specified via that variable as well (n.b. for meson the path must be
relative to the build directory):
VALGRIND=../bolt.supp meson test -C build --verbose
Coverage
--------
To analyze the current code coverage either `lcov` or `gcovr` need
to be installed. Support must also be enabled during configure time:
meson -Db_coverage=true
This should enable the general `coverage` target as well as the
`coverage-{text, html, xml}` targets:
ninja -C coverage
To manually invoke `gcovr` and exclude the `cli` directory use:
gcovr -r -e cli -s
Address Sanitizer
=================
The Address Sanitizer can be used to detect memory errors, i.e.
memory leaks and undefined behavior. Use `clang` for this, since
there the needed library (`libasan`) is dynamically loaded,
instead of pre-loaded via `LD_PRELOAD` when using `gcc`, which
conflicts with our pre-load needed for `umockdev`.
env CC=clang meson -Db_sanitize=address,undefined .
ninja -C test
NB: There might be a warning that `b_lundef` is needed as well.
It seems to work just fine right now without it.
Static analysis
===============
The clang static analyzer can be run locally via:
ninja -C scan-build
Coverity
--------
Bolt is registered with [coverity][coverity]. To submit a local build,
execute the following commands (the `cov-build` [build tool][cov-build]
must be in `PATH`) from the source directory:
CC=gcc CXX=gcc meson -Dcoverity=true coverity
cov-build --dir cov-int ninja -C coverity
tar caf bolt.xz cov-int
Upload the `bolt.xz` file to coverity for analysis. Fix defects. Profit.
[gitlab]: https://gitlab.freedesktop.org/bolt/bolt
[coverity]: https://scan.coverity.com/projects/bolt
[cov-build]: https://scan.coverity.com/download
[valgrind]: https://gist.github.com/gicmo/327dad149fcb386ac7f59e279b8ba322
bolt-0.9.2/INSTALL.md 0000664 0000000 0000000 00000001171 14174530510 0014112 0 ustar 00root root 0000000 0000000 BUILDING
========
The [meson][meson] build system is used to configure and compile bolt.
meson build # configure bolt, use build as buildir
ninja -C build # compile it
ninja -C build test # run the tests
NB: `boltd` comes with configuration files for dbus and PolicyKit that
need to be installed to the proper locations. It is probably a good
idea to manually specify them with the correct values for the current
distribution. This can be done by passing the corresponding options
to meson:
--sysconfdir=/etc
--localstatedir=/var
--sharedstatedir=/var/lib
[meson]: http://mesonbuild.com/
bolt-0.9.2/README.md 0000664 0000000 0000000 00000005244 14174530510 0013746 0 ustar 00root root 0000000 0000000 bolt
====
Userspace system daemon to enable security levels for *Thunderbolt™*
on GNU/Linux®.
Introduction
------------
Thunderbolt™ is the brand name of a hardware interface developed by
Intel® that allows the connection of external peripherals to a
computer.
Devices connected via Thunderbolt can be DMA masters and thus read
system memory without interference of the operating system (or even
the CPU). Version 3 of the interface introduced 5 different security
levels, in order to mitigate the aforementioned security risk that
connected devices pose to the system. The security level is set by the
system firmware.
The five security levels are:
* `none`: Security disabled, all devices will fully functional
on connect.
* `dponly`: Only pass the display-port stream through to the
connected device.
* `user`: Connected devices need to be manually authorized by
the user.
* `secure`: As 'user', but also challenge the device with a secret
key to verify its identity.
* `usbonly`: One PCIe tunnel is created to a usb controller in a
thunderbolt dock; no other downstream PCIe tunnels are
authorized (needs 4.17 kernel and recent hardware).
The Linux kernel, starting with version 4.13, provides an interface via
sysfs that enables userspace query the security level, the status of
connected devices and, most importantly, to authorize devices, if the
security level demands it.
boltd - the system daemon
-------------------------
The core of bolt is a system daemon (`boltd`) that interfaces with
sysfs and exposes devices via D-Bus to clients. It also has a database
of previously authorized devices (and their keys) and will, depending
on the policy set for the individual devices, automatically authorize
newly connected devices without user interaction. The daemon supports
syncing the devices database with the pre-boot access control list
firmware feature. It also adapts its behavior when iommu support is
detected.
boltctl - command line client
-----------------------------
The `boltctl` command line can be used to manage thunderbolt devices
via `boltd`. It can list devices, monitor changes and initiate
authorization of devices.
Installation
============
The [meson][meson] build system is used to configure and compile bolt.
meson build # configure bolt, use build as buildir
ninja -C build # compile it
ninja -C build test # run the tests
See [INSTALL][install] for more information, [BUGS][bugs] for how to
file issues and [HACKING][hacking] how to contribute.
[meson]: http://mesonbuild.com/
[install]: INSTALL.md
[bugs]: BUGS.md
[hacking]: HACKING.md
bolt-0.9.2/boltd/ 0000775 0000000 0000000 00000000000 14174530510 0013566 5 ustar 00root root 0000000 0000000 bolt-0.9.2/boltd/bolt-auth.c 0000664 0000000 0000000 00000023505 14174530510 0015636 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-auth.h"
#include "bolt-device.h"
#include "bolt-error.h"
#include "bolt-io.h"
#include "bolt-log.h"
#include "bolt-store.h"
#include
static void async_result_iface_init (GAsyncResultIface *iface);
struct _BoltAuth
{
GObject object;
GObject *origin;
BoltSecurity level;
BoltKey *key;
/* the device */
BoltDevice *dev;
/* result */
GError *error;
/* memory for enrollment */
BoltPolicy policy;
};
enum {
PROP_0,
PROP_ORIGIN,
PROP_LEVEL,
PROP_KEY,
PROP_DEVICE,
PROP_ERROR,
PROP_POLICY,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = { NULL, };
G_DEFINE_TYPE_WITH_CODE (BoltAuth, bolt_auth, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, async_result_iface_init));
static void
bolt_auth_finalize (GObject *object)
{
BoltAuth *auth = BOLT_AUTH (object);
g_clear_object (&auth->origin);
g_clear_object (&auth->key);
g_clear_object (&auth->dev);
g_clear_error (&auth->error);
G_OBJECT_CLASS (bolt_auth_parent_class)->finalize (object);
}
static void
bolt_auth_init (BoltAuth *auth)
{
}
static void
bolt_auth_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltAuth *auth = BOLT_AUTH (object);
switch (prop_id)
{
case PROP_ORIGIN:
g_value_set_object (value, auth->origin);
break;
case PROP_LEVEL:
g_value_set_enum (value, auth->level);
break;
case PROP_KEY:
g_value_set_object (value, auth->key);
break;
case PROP_DEVICE:
g_value_set_object (value, auth->dev);
break;
case PROP_POLICY:
g_value_set_enum (value, auth->policy);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_auth_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltAuth *auth = BOLT_AUTH (object);
switch (prop_id)
{
case PROP_ORIGIN:
auth->origin = g_value_dup_object (value);
break;
case PROP_LEVEL:
auth->level = g_value_get_enum (value);
break;
case PROP_KEY:
auth->key = g_value_dup_object (value);
break;
case PROP_DEVICE:
g_return_if_fail (auth->dev == NULL);
auth->dev = g_value_dup_object (value);
break;
case PROP_POLICY:
auth->policy = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_auth_class_init (BoltAuthClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = bolt_auth_finalize;
gobject_class->get_property = bolt_auth_get_property;
gobject_class->set_property = bolt_auth_set_property;
props[PROP_ORIGIN] =
g_param_spec_object ("origin",
NULL, NULL,
G_TYPE_OBJECT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NICK);
props[PROP_LEVEL] =
g_param_spec_enum ("level",
NULL, NULL,
BOLT_TYPE_SECURITY,
BOLT_SECURITY_NONE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NICK);
props[PROP_KEY] =
g_param_spec_object ("key",
NULL, NULL,
BOLT_TYPE_KEY,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NICK);
props[PROP_DEVICE] =
g_param_spec_object ("device",
NULL, NULL,
BOLT_TYPE_DEVICE,
G_PARAM_READWRITE |
G_PARAM_STATIC_NICK);
props[PROP_ERROR] =
g_param_spec_boxed ("error", NULL, NULL,
G_TYPE_ERROR,
G_PARAM_READWRITE |
G_PARAM_STATIC_NICK);
props[PROP_POLICY] =
g_param_spec_enum ("policy",
NULL, NULL,
BOLT_TYPE_POLICY,
BOLT_POLICY_UNKNOWN,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
}
/* async result methods */
static gpointer
async_result_get_user_data (GAsyncResult *res)
{
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
return NULL;
}
static GObject *
async_result_get_source_object (GAsyncResult *res)
{
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
return G_OBJECT (BOLT_AUTH (res)->dev);
}
static gboolean
async_result_is_tagged (GAsyncResult *res,
gpointer source_tag)
{
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
return FALSE;
}
static void
async_result_iface_init (GAsyncResultIface *iface)
{
iface->get_source_object = async_result_get_source_object;
iface->get_user_data = async_result_get_user_data;
iface->is_tagged = async_result_is_tagged;
}
/* public methods */
BoltAuth *
bolt_auth_new (gpointer origin,
BoltSecurity level,
BoltKey *key)
{
BoltAuth *auth;
auth = g_object_new (BOLT_TYPE_AUTH,
"origin", origin,
"level", level,
"key", key,
NULL);
return auth;
}
void
bolt_auth_return_new_error (BoltAuth *auth,
GQuark domain,
gint code,
const char *format,
...)
{
va_list args;
g_return_if_fail (BOLT_IS_AUTH (auth));
va_start (args, format);
auth->error = g_error_new_valist (domain, code, format, args);
va_end (args);
}
void
bolt_auth_return_error (BoltAuth *auth,
GError **error)
{
g_return_if_fail (BOLT_IS_AUTH (auth));
g_return_if_fail (error != NULL && *error != NULL);
g_return_if_fail (auth->error == NULL);
auth->error = g_steal_pointer (error);
}
gboolean
bolt_auth_check (BoltAuth *auth,
GError **error)
{
g_return_val_if_fail (BOLT_IS_AUTH (auth), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (auth->error)
{
g_autoptr(GError) err = g_error_copy (auth->error);
return bolt_error_propagate (error, &err);
}
return TRUE;
}
BoltDevice *
bolt_auth_get_device (BoltAuth *auth)
{
g_return_val_if_fail (BOLT_IS_AUTH (auth), NULL);
return auth->dev;
}
BoltSecurity
bolt_auth_get_level (BoltAuth *auth)
{
g_return_val_if_fail (BOLT_IS_AUTH (auth), BOLT_SECURITY_UNKNOWN);
return auth->level;
}
BoltKey *
bolt_auth_get_key (BoltAuth *auth)
{
g_return_val_if_fail (BOLT_IS_AUTH (auth), NULL);
return auth->key;
}
BoltKeyState
bolt_auth_get_keystate (BoltAuth *auth)
{
g_return_val_if_fail (BOLT_IS_AUTH (auth), BOLT_KEY_MISSING);
return bolt_key_get_state (auth->key);
}
gboolean
bolt_auth_has_key (BoltAuth *auth)
{
g_return_val_if_fail (BOLT_IS_AUTH (auth), FALSE);
return auth->key != NULL;
}
gpointer
bolt_auth_get_origin (BoltAuth *auth)
{
g_return_val_if_fail (BOLT_IS_AUTH (auth), NULL);
return auth->origin;
}
BoltPolicy
bolt_auth_get_policy (BoltAuth *auth)
{
g_return_val_if_fail (BOLT_IS_AUTH (auth), BOLT_POLICY_UNKNOWN);
return auth->policy;
}
void
bolt_auth_set_policy (BoltAuth *auth,
BoltPolicy policy)
{
g_return_if_fail (BOLT_IS_AUTH (auth));
if (auth->policy == policy)
return;
auth->policy = policy;
g_object_notify_by_pspec (G_OBJECT (auth), props[PROP_POLICY]);
}
BoltStatus
bolt_auth_to_status (BoltAuth *auth)
{
g_return_val_if_fail (BOLT_IS_AUTH (auth), BOLT_STATUS_UNKNOWN);
if (auth->error != NULL)
return BOLT_STATUS_AUTH_ERROR;
switch (auth->level)
{
case BOLT_SECURITY_SECURE:
case BOLT_SECURITY_USER:
return BOLT_STATUS_AUTHORIZED;
case BOLT_SECURITY_DPONLY:
case BOLT_SECURITY_USBONLY:
case BOLT_SECURITY_NONE:
bolt_bug ("unexpected security in BoltAuth::level: %s",
bolt_security_to_string (auth->level));
return BOLT_STATUS_AUTHORIZED;
case BOLT_SECURITY_UNKNOWN:
bolt_bug ("unknown status in BoltAUth::level");
return BOLT_STATUS_UNKNOWN;
}
return BOLT_STATUS_UNKNOWN;
}
BoltAuthFlags
bolt_auth_to_flags (BoltAuth *auth,
BoltAuthFlags *mask)
{
BoltKeyState ks;
g_return_val_if_fail (BOLT_IS_AUTH (auth), 0);
if (mask)
*mask = 0;
if (auth->error != NULL)
return 0;
if (auth->level != BOLT_SECURITY_SECURE)
return 0;
if (mask)
*mask = BOLT_AUTH_SECURE;
ks = bolt_key_get_state (auth->key);
if (ks == BOLT_KEY_NEW)
return 0;
return BOLT_AUTH_SECURE;
}
bolt-0.9.2/boltd/bolt-auth.h 0000664 0000000 0000000 00000004676 14174530510 0015653 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-key.h"
#include
G_BEGIN_DECLS
/* forward decl because bolt-device.h include bolt-auth.h */
typedef struct _BoltDevice BoltDevice;
#define BOLT_TYPE_AUTH bolt_auth_get_type ()
G_DECLARE_FINAL_TYPE (BoltAuth, bolt_auth, BOLT, AUTH, GObject);
BoltAuth * bolt_auth_new (gpointer origin,
BoltSecurity level,
BoltKey *key);
void bolt_auth_return_new_error (BoltAuth *auth,
GQuark domain,
gint code,
const char *format,
...) G_GNUC_PRINTF (4, 5);
void bolt_auth_return_error (BoltAuth *auth,
GError **error);
gboolean bolt_auth_check (BoltAuth *auth,
GError **error);
BoltDevice * bolt_auth_get_device (BoltAuth *auth);
BoltSecurity bolt_auth_get_level (BoltAuth *auth);
BoltKey * bolt_auth_get_key (BoltAuth *auth);
BoltKeyState bolt_auth_get_keystate (BoltAuth *auth);
gboolean bolt_auth_has_key (BoltAuth *auth);
gpointer bolt_auth_get_origin (BoltAuth *auth);
BoltPolicy bolt_auth_get_policy (BoltAuth *auth);
void bolt_auth_set_policy (BoltAuth *auth,
BoltPolicy policy);
BoltStatus bolt_auth_to_status (BoltAuth *auth);
BoltAuthFlags bolt_auth_to_flags (BoltAuth *auth,
BoltAuthFlags *mask);
G_END_DECLS
bolt-0.9.2/boltd/bolt-bouncer.c 0000664 0000000 0000000 00000020540 14174530510 0016326 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-bouncer.h"
#include "bolt-log.h"
#include "bolt-str.h"
#include "bolt-exported.h"
#include
#include
static void bouncer_initable_iface_init (GInitableIface *iface);
static gboolean bouncer_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error);
#ifndef HAVE_POLKIT_AUTOPTR
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitDetails, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitSubject, g_object_unref)
#endif
struct _BoltBouncer
{
GObject object;
/* */
PolkitAuthority *authority;
};
G_DEFINE_TYPE_WITH_CODE (BoltBouncer, bolt_bouncer, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
bouncer_initable_iface_init));
static void
bolt_bouncer_finalize (GObject *object)
{
BoltBouncer *bouncer = BOLT_BOUNCER (object);
g_clear_object (&bouncer->authority);
G_OBJECT_CLASS (bolt_bouncer_parent_class)->finalize (object);
}
static void
bolt_bouncer_init (BoltBouncer *bouncer)
{
}
static void
bolt_bouncer_class_init (BoltBouncerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = bolt_bouncer_finalize;
}
static void
bouncer_initable_iface_init (GInitableIface *iface)
{
iface->init = bouncer_initialize;
}
static gboolean
bouncer_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
BoltBouncer *bnc = BOLT_BOUNCER (initable);
bolt_info (LOG_TOPIC ("bouncer"), "initializing polkit");
bnc->authority = polkit_authority_get_sync (cancellable, error);
return bnc->authority != NULL;
}
/* internal methods */
static gboolean
bolt_bouncer_check_action (BoltBouncer *bnc,
GDBusMethodInvocation *inv,
const char *action,
gboolean *authorized,
GError **error)
{
g_autoptr(PolkitSubject) subject = NULL;
g_autoptr(PolkitDetails) details = NULL;
g_autoptr(PolkitAuthorizationResult) res = NULL;
PolkitCheckAuthorizationFlags flags;
const char *sender;
sender = g_dbus_method_invocation_get_sender (inv);
subject = polkit_system_bus_name_new (sender);
details = polkit_details_new ();
flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
res = polkit_authority_check_authorization_sync (bnc->authority,
subject,
action, details,
flags,
NULL, error);
if (res == NULL)
return FALSE;
*authorized = polkit_authorization_result_get_is_authorized (res);
return TRUE;
}
static gboolean
handle_authorize_method (BoltExported *exported,
GDBusMethodInvocation *inv,
GError **error,
gpointer user_data)
{
g_autoptr(PolkitSubject) subject = NULL;
g_autoptr(PolkitDetails) details = NULL;
gboolean authorized = FALSE;
BoltBouncer *bnc;
const char *method_name;
const char *sender;
const char *action;
bnc = BOLT_BOUNCER (user_data);
method_name = g_dbus_method_invocation_get_method_name (inv);
sender = g_dbus_method_invocation_get_sender (inv);
subject = polkit_system_bus_name_new (sender);
details = polkit_details_new ();
action = NULL;
if (bolt_streq (method_name, "EnrollDevice"))
action = "org.freedesktop.bolt.enroll";
else if (bolt_streq (method_name, "Authorize"))
action = "org.freedesktop.bolt.authorize";
else if (bolt_streq (method_name, "ForgetDevice"))
action = "org.freedesktop.bolt.manage";
else if (bolt_streq (method_name, "ForcePower"))
action = "org.freedesktop.bolt.manage";
else if (bolt_streq (method_name, "ListDomains"))
authorized = TRUE;
else if (bolt_streq (method_name, "DomainById"))
authorized = TRUE;
else if (bolt_streq (method_name, "ListDevices"))
authorized = TRUE;
else if (bolt_streq (method_name, "DeviceByUid"))
authorized = TRUE;
else if (bolt_streq (method_name, "ListGuards"))
authorized = TRUE;
if (!authorized && action)
{
PolkitCheckAuthorizationFlags flags;
g_autoptr(PolkitAuthorizationResult) res = NULL;
flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
res = polkit_authority_check_authorization_sync (bnc->authority,
subject,
action, details,
flags,
NULL, error);
if (res == NULL)
return FALSE;
authorized = polkit_authorization_result_get_is_authorized (res);
}
if (authorized == FALSE)
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
"Bolt operation '%s' not allowed for user",
method_name);
return authorized;
}
static gboolean
handle_authorize_property (BoltExported *exported,
const char *name,
gboolean setting,
GDBusMethodInvocation *inv,
GError **error,
gpointer user_data)
{
const char *type_name = G_OBJECT_TYPE_NAME (exported);
const char *action = NULL;
gboolean authorized = FALSE;
BoltBouncer *bnc;
bnc = BOLT_BOUNCER (user_data);
if (bolt_streq (type_name, "BoltDevice"))
{
if (bolt_streq (name, "label"))
action = "org.freedesktop.bolt.manage";
else if (bolt_streq (name, "policy"))
action = "org.freedesktop.bolt.manage";
}
else if (bolt_streq (type_name, "BoltDomain"))
{
if (bolt_streq (name, "bootacl"))
action = "org.freedesktop.bolt.manage";
}
else if (bolt_streq (type_name, "BoltManager"))
{
if (bolt_streq (name, "auth-mode"))
action = "org.freedesktop.bolt.manage";
}
if (!authorized && action)
{
gboolean ok;
ok = bolt_bouncer_check_action (bnc,
inv,
action,
&authorized,
error);
if (!ok)
return FALSE;
}
if (authorized == FALSE)
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
"Setting property of '%s.%s' not allowed for user",
type_name, name);
return authorized;
}
/* public methods */
BoltBouncer *
bolt_bouncer_new (GCancellable *cancellable,
GError **error)
{
return g_initable_new (BOLT_TYPE_BOUNCER,
cancellable, error,
NULL);
}
void
bolt_bouncer_add_client (BoltBouncer *bnc,
gpointer client)
{
g_return_if_fail (BOLT_IS_BOUNCER (bnc));
g_return_if_fail (BOLT_IS_EXPORTED (client));
g_signal_connect_object (client, "authorize-method",
G_CALLBACK (handle_authorize_method),
bnc, 0);
g_signal_connect_object (client, "authorize-property",
G_CALLBACK (handle_authorize_property),
bnc, 0);
}
bolt-0.9.2/boltd/bolt-bouncer.h 0000664 0000000 0000000 00000002263 14174530510 0016335 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
#define BOLT_TYPE_BOUNCER bolt_bouncer_get_type ()
G_DECLARE_FINAL_TYPE (BoltBouncer, bolt_bouncer, BOLT, BOUNCER, GObject);
BoltBouncer * bolt_bouncer_new (GCancellable *cancellable,
GError **error);
void bolt_bouncer_add_client (BoltBouncer *bnc,
gpointer client);
G_END_DECLS
bolt-0.9.2/boltd/bolt-config.c 0000664 0000000 0000000 00000007432 14174530510 0016143 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-enums.h"
#include "bolt-error.h"
#include "bolt-names.h"
#include "bolt-config.h"
#define DAEMON_GROUP "config"
#define CFG_VERSION 1
#define DEFAULT_POLICY_KEY "DefaultPolicy"
#define AUTH_MODE_KEY "AuthMode"
const char *
bolt_get_store_path (void)
{
const char *path;
/* set by tools or the user directly */
path = g_getenv (BOLT_ENV_DBPATH);
if (path != NULL)
return path;
/* set by systemd >= 240 to an absolute path
* taking into account the StateDirectory
* unit file setting */
path = g_getenv (BOLT_ENV_STATE_DIRECTORY);
if (path != NULL)
return path;
/* set at compile time (config.h) */
return BOLT_DBDIR;
}
const char *
bolt_get_runtime_directory (void)
{
const char *path;
path = g_getenv (BOLT_ENV_RUNTIME_DIRECTORY);
if (path)
return path;
return "/run/boltd";
}
GKeyFile *
bolt_config_user_init (void)
{
GKeyFile *cfg;
cfg = g_key_file_new ();
g_key_file_set_comment (cfg, NULL, NULL,
" Generated by boltd - do not edit",
NULL);
g_key_file_set_uint64 (cfg, DAEMON_GROUP, "version", CFG_VERSION);
return cfg;
}
BoltTri
bolt_config_load_default_policy (GKeyFile *cfg,
BoltPolicy *policy,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *str = NULL;
BoltPolicy p;
g_return_val_if_fail (error == NULL || *error == NULL, TRI_NO);
g_return_val_if_fail (policy != NULL, TRI_NO);
if (cfg == NULL)
return TRI_NO;
str = g_key_file_get_string (cfg, DAEMON_GROUP, DEFAULT_POLICY_KEY, &err);
if (str == NULL)
{
int res = bolt_err_notfound (err) ? TRI_NO : TRI_ERROR;
if (res == TRI_ERROR)
bolt_error_propagate (error, &err);
return res;
}
p = bolt_policy_from_string (str);
if (!bolt_policy_validate (p))
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_CFG,
"invalid policy: %s", str);
return TRI_ERROR;
}
*policy = p;
return TRI_YES;
}
BoltTri
bolt_config_load_auth_mode (GKeyFile *cfg,
BoltAuthMode *authmode,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *str = NULL;
guint flags = 0;
gboolean ok;
if (cfg == NULL)
return TRI_NO;
g_return_val_if_fail (error == NULL || *error == NULL, TRI_NO);
str = g_key_file_get_string (cfg, DAEMON_GROUP, AUTH_MODE_KEY, &err);
if (str == NULL)
{
int res = bolt_err_notfound (err) ? TRI_NO : TRI_ERROR;
if (res == TRI_ERROR)
bolt_error_propagate (error, &err);
return res;
}
ok = bolt_flags_from_string (BOLT_TYPE_AUTH_MODE, str, &flags, error);
if (!ok)
return TRI_ERROR;
if (authmode)
*authmode = flags;
return TRI_YES;
}
void
bolt_config_set_auth_mode (GKeyFile *cfg,
const char *authmode)
{
g_return_if_fail (cfg != NULL);
g_key_file_set_string (cfg, DAEMON_GROUP, AUTH_MODE_KEY, authmode);
}
bolt-0.9.2/boltd/bolt-config.h 0000664 0000000 0000000 00000003147 14174530510 0016147 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include "bolt-enums.h"
G_BEGIN_DECLS
/* well known paths */
const char * bolt_get_store_path (void);
const char * bolt_get_runtime_directory (void);
/* user and system configuration */
typedef enum BoltTri {
TRI_ERROR = -1,
TRI_NO = 0,
TRI_YES = 1,
} BoltTri;
GKeyFile * bolt_config_user_init (void);
BoltTri bolt_config_load_default_policy (GKeyFile *cfg,
BoltPolicy *policy,
GError **error);
BoltTri bolt_config_load_auth_mode (GKeyFile *cfg,
BoltAuthMode *authmode,
GError **error);
void bolt_config_set_auth_mode (GKeyFile *cfg,
const char *authmode);
G_END_DECLS
bolt-0.9.2/boltd/bolt-daemon.c 0000664 0000000 0000000 00000016562 14174530510 0016145 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-dbus.h"
#include "bolt-log.h"
#include "bolt-manager.h"
#include "bolt-names.h"
#include "bolt-str.h"
#include "bolt-term.h"
#include
#include
#include
#include
#include
#include
#include
/* globals */
static BoltManager *manager = NULL;
static GMainLoop *main_loop = NULL;
static guint name_owner_id = 0;
static guint sigterm_id = 0;
static gboolean
handle_sigterm (gpointer user_data)
{
bolt_debug (LOG_TOPIC ("signal"), "got SIGTERM; shutting down...");
if (g_main_loop_is_running (main_loop))
g_main_loop_quit (main_loop);
sigterm_id = 0;
return G_SOURCE_REMOVE;
}
static void
install_signal_hanlder (void)
{
g_autoptr(GSource) source = NULL;
source = g_unix_signal_source_new (SIGTERM);
if (source == NULL)
{
bolt_warn (LOG_TOPIC ("signal"), "failed installing SIGTERM handler: %s",
g_strerror (errno));
return;
}
g_source_set_callback (source, handle_sigterm, NULL, NULL);
sigterm_id = g_source_attach (source, NULL);
bolt_debug (LOG_TOPIC ("signal"), "SIGTERM handler installed [%u]",
sigterm_id);
}
typedef struct _LogCfg
{
gboolean debug;
gboolean journal;
char session_id[33];
} LogCfg;
static GLogWriterOutput
daemon_logger (GLogLevelFlags level,
const GLogField *fields,
gsize n_fields,
gpointer user_data)
{
g_autoptr(BoltLogCtx) ctx = NULL;
GLogWriterOutput res = G_LOG_WRITER_UNHANDLED;
LogCfg *log = user_data;
g_return_val_if_fail (fields != NULL, G_LOG_WRITER_UNHANDLED);
g_return_val_if_fail (n_fields > 0, G_LOG_WRITER_UNHANDLED);
ctx = bolt_log_ctx_acquire (fields, n_fields);
if (ctx == NULL)
return G_LOG_WRITER_UNHANDLED;
/* replace the log context field with the session id */
bolt_log_ctx_set_id (ctx, log->session_id);
if (level & G_LOG_LEVEL_DEBUG && log->debug == FALSE)
{
const char *domain = blot_log_ctx_get_domain (ctx);
const char *env = g_getenv ("G_MESSAGES_DEBUG");
if (!env || !domain || !strstr (env, domain))
return G_LOG_WRITER_UNHANDLED;
}
if (fileno (stderr) < 0)
return G_LOG_WRITER_UNHANDLED;
if (log->journal || g_log_writer_is_journald (fileno (stderr)))
res = bolt_log_journal (ctx, level, 0);
if (res == G_LOG_WRITER_UNHANDLED)
res = bolt_log_stdstream (ctx, level, 0);
return res;
}
static void
on_bus_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
bolt_debug (LOG_TOPIC ("dbus"), "got the bus [%s]", name);
/* */
manager = g_initable_new (BOLT_TYPE_MANAGER,
NULL, &error,
NULL);
if (manager == NULL)
{
bolt_error (LOG_ERR (error), "could not create manager");
exit (EXIT_FAILURE);
}
if (!bolt_manager_export (manager, connection, &error))
bolt_warn_err (error, LOG_TOPIC ("dbus"), "error exporting the manager");
}
static void
on_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
bolt_debug (LOG_TOPIC ("dbus"), "got the name");
bolt_manager_got_the_name (manager);
}
static void
on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
bolt_debug (LOG_TOPIC ("dbus"), "name lost; shutting down...");
if (g_main_loop_is_running (main_loop))
g_main_loop_quit (main_loop);
}
int
main (int argc, char **argv)
{
g_autoptr(GOptionContext) context = NULL;
g_autoptr(GError) error = NULL;
gboolean replace = FALSE;
gboolean show_version = FALSE;
gboolean session_bus = FALSE;
GBusType bus_type = G_BUS_TYPE_SYSTEM;
GBusNameOwnerFlags flags;
LogCfg log = { FALSE, FALSE, };
const GOptionEntry options[] = {
{ "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "Replace old daemon.", NULL },
{ "session-bus", 0, 0, G_OPTION_ARG_NONE, &session_bus, "Use the session bus.", NULL},
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &log.debug, "Enable debug output.", NULL },
{ "journal", 0, 0, G_OPTION_ARG_NONE, &log.journal, "Force logging to the journal.", NULL},
{ "version", 0, 0, G_OPTION_ARG_NONE, &show_version, "Print daemon version.", NULL},
{ NULL }
};
install_signal_hanlder ();
setlocale (LC_ALL, "");
g_setenv ("GIO_USE_VFS", "local", TRUE);
g_set_prgname (argv[0]);
g_log_set_writer_func (daemon_logger, &log, NULL);
context = g_option_context_new ("");
g_option_context_set_summary (context, "Thunderbolt system daemon");
g_option_context_add_main_entries (context, options, PACKAGE_NAME);
if (!g_option_context_parse (context, &argc, &argv, &error))
{
g_printerr ("%s: %s", g_get_application_name (), error->message);
g_printerr ("\n");
g_printerr ("Try \"%s --help\" for more information.",
g_get_prgname ());
g_printerr ("\n");
g_option_context_free (context);
return EXIT_FAILURE;
}
if (show_version)
{
g_print (PACKAGE_NAME " " PACKAGE_VERSION "\n");
return EXIT_SUCCESS;
}
/* setup logging */
if (log.debug == FALSE && g_getenv ("G_MESSAGES_DEBUG"))
{
const char *domains = g_getenv ("G_MESSAGES_DEBUG");
log.debug = bolt_streq (domains, "all");
}
bolt_log_gen_id (log.session_id);
bolt_dbus_ensure_resources ();
bolt_msg (LOG_DIRECT (BOLT_LOG_VERSION, PACKAGE_VERSION),
LOG_ID (STARTUP),
PACKAGE_NAME " " PACKAGE_VERSION " starting up.");
bolt_debug ("session id is %s", log.session_id);
/* hop on the bus, Gus */
flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
if (replace)
flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
if (session_bus)
bus_type = G_BUS_TYPE_SESSION;
name_owner_id = g_bus_own_name (bus_type,
BOLT_DBUS_NAME,
flags,
on_bus_acquired,
on_name_acquired,
on_name_lost,
NULL,
NULL);
main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (main_loop);
/* When all is said and done, more is said then done. */
g_main_loop_unref (main_loop);
/* we are shutting down */
if (name_owner_id > 0)
{
g_bus_unown_name (name_owner_id);
name_owner_id = 0;
}
g_clear_object (&manager);
bolt_debug ("shutdown complete");
return EXIT_SUCCESS;
}
bolt-0.9.2/boltd/bolt-device.c 0000664 0000000 0000000 00000130323 14174530510 0016131 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-device.h"
#include "bolt-domain.h"
#include "bolt-enums.h"
#include "bolt-error.h"
#include "bolt-glue.h"
#include "bolt-io.h"
#include "bolt-log.h"
#include "bolt-manager.h"
#include "bolt-names.h"
#include "bolt-store.h"
#include "bolt-str.h"
#include "bolt-sysfs.h"
#include "bolt-time.h"
#include
#include
/* internal methods */
static void device_set_status_internal (BoltDevice *dev,
BoltStatus status,
gboolean notify);
/* dbus property setter */
static gboolean handle_set_label (BoltExported *obj,
const char *name,
const GValue *value,
GError **error);
static gboolean handle_set_policy (BoltExported *obj,
const char *name,
const GValue *value,
GError **error);
/* dbus method calls */
static GVariant * handle_authorize (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error);
struct _BoltDevice
{
BoltExported object;
/* device props */
char *uid;
char *name;
char *vendor;
guint gen;
BoltDeviceType type;
BoltStatus status;
/* when device is attached */
BoltAuthFlags aflags;
char *syspath;
BoltDomain *domain;
char *parent;
guint64 conntime;
guint64 authtime;
BoltLinkSpeed linkspeed;
/* when device is stored */
BoltStore *store;
BoltPolicy policy;
BoltKeyState key;
guint64 storetime;
char *label;
};
enum {
PROP_0,
/* internal properties */
PROP_OBJECT_ID,
PROP_STORE,
PROP_SECURITY,
/* exported properties start here, */
PROP_UID,
PROP_NAME,
PROP_VENDOR,
PROP_GEN,
PROP_TYPE,
PROP_STATUS,
PROP_AUTHFLAGS,
PROP_PARENT,
PROP_SYSFS,
PROP_DOMAIN,
PROP_CONNTIME,
PROP_AUTHTIME,
PROP_LINKSPEED,
PROP_STORED,
PROP_POLICY,
PROP_HAVE_KEY,
PROP_STORETIME,
PROP_LABEL,
PROP_LAST,
PROP_EXPORTED = PROP_UID
};
static GParamSpec *props[PROP_LAST] = {NULL, };
enum {
SIGNAL_STATUS_CHANGED,
SIGNAL_LAST
};
static guint signals[SIGNAL_LAST] = {0};
G_DEFINE_TYPE (BoltDevice,
bolt_device,
BOLT_TYPE_EXPORTED)
static void
bolt_device_finalize (GObject *object)
{
BoltDevice *dev = BOLT_DEVICE (object);
g_clear_object (&dev->store);
g_free (dev->uid);
g_free (dev->name);
g_free (dev->vendor);
g_free (dev->parent);
g_free (dev->syspath);
g_clear_object (&dev->domain);
g_free (dev->label);
G_OBJECT_CLASS (bolt_device_parent_class)->finalize (object);
}
static void
bolt_device_init (BoltDevice *dev)
{
}
static void
bolt_device_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltDevice *dev = BOLT_DEVICE (object);
switch (prop_id)
{
case PROP_STORE:
g_value_set_object (value, dev->store);
break;
case PROP_SECURITY:
{
BoltSecurity sl = BOLT_SECURITY_UNKNOWN;
if (dev->domain)
sl = bolt_domain_get_security (dev->domain);
g_value_set_enum (value, sl);
}
break;
case PROP_OBJECT_ID:
case PROP_UID:
g_value_set_string (value, dev->uid);
break;
case PROP_NAME:
g_value_set_string (value, dev->name);
break;
case PROP_TYPE:
g_value_set_enum (value, dev->type);
break;
case PROP_VENDOR:
g_value_set_string (value, dev->vendor);
break;
case PROP_STATUS:
g_value_set_enum (value, dev->status);
break;
case PROP_GEN:
g_value_set_uint (value, dev->gen);
break;
case PROP_AUTHFLAGS:
g_value_set_flags (value, dev->aflags);
break;
case PROP_PARENT:
g_value_set_string (value, dev->parent);
break;
case PROP_SYSFS:
g_value_set_string (value, dev->syspath);
break;
case PROP_DOMAIN:
g_value_set_object (value, dev->domain);
break;
case PROP_CONNTIME:
g_value_set_uint64 (value, dev->conntime);
break;
case PROP_AUTHTIME:
g_value_set_uint64 (value, dev->authtime);
break;
case PROP_LINKSPEED:
g_value_set_boxed (value, &dev->linkspeed);
break;
case PROP_STORED:
g_value_set_boolean (value, dev->store != NULL);
break;
case PROP_POLICY:
g_value_set_enum (value, dev->policy);
break;
case PROP_HAVE_KEY:
g_value_set_enum (value, dev->key);
break;
case PROP_STORETIME:
g_value_set_uint64 (value, dev->storetime);
break;
case PROP_LABEL:
g_value_set_string (value, dev->label);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_device_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltDevice *dev = BOLT_DEVICE (object);
switch (prop_id)
{
case PROP_STORE:
dev->store = g_value_dup_object (value);
g_object_notify_by_pspec (object, props[PROP_STORED]);
break;
case PROP_UID:
g_return_if_fail (dev->uid == NULL);
dev->uid = g_value_dup_string (value);
break;
case PROP_NAME:
g_clear_pointer (&dev->name, g_free);
dev->name = g_value_dup_string (value);
break;
case PROP_VENDOR:
g_clear_pointer (&dev->vendor, g_free);
dev->vendor = g_value_dup_string (value);
break;
case PROP_GEN:
dev->gen = g_value_get_uint (value);
break;
case PROP_TYPE:
dev->type = g_value_get_enum (value);
break;
case PROP_STATUS:
{
BoltStatus now = g_value_get_enum (value);
device_set_status_internal (dev, now, FALSE);
break;
}
case PROP_AUTHFLAGS:
dev->aflags = g_value_get_flags (value);
break;
case PROP_PARENT:
g_clear_pointer (&dev->parent, g_free);
dev->parent = g_value_dup_string (value);
break;
case PROP_SYSFS:
g_clear_pointer (&dev->syspath, g_free);
dev->syspath = g_value_dup_string (value);
break;
case PROP_DOMAIN:
g_clear_object (&dev->domain);
dev->domain = g_value_dup_object (value);
break;
case PROP_CONNTIME:
dev->conntime = g_value_get_uint64 (value);
break;
case PROP_AUTHTIME:
dev->authtime = g_value_get_uint64 (value);
break;
case PROP_LINKSPEED:
{
BoltLinkSpeed *li = g_value_get_boxed (value);
dev->linkspeed = *li;
}
break;
case PROP_POLICY:
dev->policy = g_value_get_enum (value);
break;
case PROP_HAVE_KEY:
dev->key = g_value_get_enum (value);
break;
case PROP_STORETIME:
dev->storetime = g_value_get_uint64 (value);
break;
case PROP_LABEL:
g_clear_pointer (&dev->label, g_free);
dev->label = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_device_class_init (BoltDeviceClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
BoltExportedClass *exported_class = BOLT_EXPORTED_CLASS (klass);
gobject_class->finalize = bolt_device_finalize;
gobject_class->get_property = bolt_device_get_property;
gobject_class->set_property = bolt_device_set_property;
props[PROP_OBJECT_ID] =
bolt_param_spec_override (gobject_class, "object-id");
props[PROP_STORE] =
g_param_spec_object ("store",
NULL, NULL,
BOLT_TYPE_STORE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_SECURITY] =
g_param_spec_enum ("security",
"Security", NULL,
BOLT_TYPE_SECURITY,
BOLT_SECURITY_NONE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_UID] =
g_param_spec_string ("uid",
"Uid", NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_NAME] =
g_param_spec_string ("name",
"Name", NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_VENDOR] =
g_param_spec_string ("vendor",
"Vendor", NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_GEN] =
g_param_spec_uint ("generation",
"Generation",
NULL,
0, G_MAXUINT, 0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_TYPE] =
g_param_spec_enum ("type",
"Type", NULL,
BOLT_TYPE_DEVICE_TYPE,
BOLT_DEVICE_PERIPHERAL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_STATUS] =
g_param_spec_enum ("status",
"Status", NULL,
BOLT_TYPE_STATUS,
BOLT_STATUS_DISCONNECTED,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_AUTHFLAGS] =
g_param_spec_flags ("authflags",
"AuthFlags", NULL,
BOLT_TYPE_AUTH_FLAGS,
BOLT_AUTH_NONE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_LINKSPEED] =
g_param_spec_boxed ("linkspeed",
"LinkSpeed", NULL,
BOLT_TYPE_LINK_SPEED,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_PARENT] =
g_param_spec_string ("parent",
"Parent", NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_SYSFS] =
g_param_spec_string ("sysfs-path",
"SysfsPath", NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_DOMAIN] =
g_param_spec_object ("domain",
"Domain", NULL,
BOLT_TYPE_DOMAIN,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_CONNTIME] =
g_param_spec_uint64 ("conntime",
"ConnectTime", NULL,
0, G_MAXUINT64, 0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_AUTHTIME] =
g_param_spec_uint64 ("authtime",
"AuthorizeTime", NULL,
0, G_MAXUINT64, 0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_STORED] =
g_param_spec_boolean ("stored",
"Stored", NULL,
FALSE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_POLICY] =
g_param_spec_enum ("policy",
"Policy", NULL,
BOLT_TYPE_POLICY,
BOLT_POLICY_DEFAULT,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_HAVE_KEY] =
g_param_spec_enum ("key",
"Key", NULL,
BOLT_TYPE_KEY_STATE,
BOLT_KEY_MISSING,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_STORETIME] =
g_param_spec_uint64 ("storetime",
"StoreTime", NULL,
0, G_MAXUINT64, 0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_LABEL] =
g_param_spec_string ("label",
"Label", NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
signals[SIGNAL_STATUS_CHANGED] =
g_signal_new ("status-changed",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
1, BOLT_TYPE_STATUS);
bolt_exported_class_set_interface_info (exported_class,
BOLT_DBUS_DEVICE_INTERFACE,
BOLT_DBUS_GRESOURCE_PATH);
bolt_exported_class_set_object_path (exported_class,
BOLT_DBUS_PATH_DEVICES);
bolt_exported_class_export_properties (exported_class,
PROP_EXPORTED,
PROP_LAST,
props);
bolt_exported_class_property_setter (exported_class,
props[PROP_LABEL],
handle_set_label);
bolt_exported_class_property_setter (exported_class,
props[PROP_POLICY],
handle_set_policy);
bolt_exported_class_export_method (exported_class,
"Authorize",
handle_authorize);
bolt_exported_class_property_wireconv (exported_class,
props[PROP_LINKSPEED],
"linkspeed-as-variant",
bolt_link_speed_to_wire,
bolt_link_speed_from_wire);
}
/* internal methods */
static void
device_set_status_internal (BoltDevice *dev,
BoltStatus status,
gboolean notify)
{
BoltStatus before;
before = dev->status;
if (before == status)
return;
dev->status = status;
g_signal_emit (dev, signals[SIGNAL_STATUS_CHANGED], 0, before);
if (notify)
g_object_notify_by_pspec (G_OBJECT (dev), props[PROP_STATUS]);
}
static BoltStatus
bolt_status_from_info (BoltDevInfo *info)
{
gint authorized;
gboolean have_key;
authorized = info->authorized;
have_key = info->keysize > 0;
if (authorized < 0)
return BOLT_STATUS_UNKNOWN;
else if (authorized > 0)
return BOLT_STATUS_AUTHORIZED;
if (have_key) /* authorized == 0 */
return BOLT_STATUS_AUTH_ERROR;
return BOLT_STATUS_CONNECTED;
}
static BoltAuthFlags
bolt_auth_flags_from_info (BoltDevInfo *info,
BoltDomain *domain,
BoltAuthFlags *mask)
{
BoltSecurity sl;
guint val = 0;
guint msk = 0;
g_return_val_if_fail (info != NULL, 0);
g_return_val_if_fail (domain != NULL, 0);
msk |= BOLT_AUTH_SECURE;
if (info->authorized == 2)
val |= BOLT_AUTH_SECURE;
sl = bolt_domain_get_security (domain);
if (sl == BOLT_SECURITY_SECURE)
{
msk |= BOLT_AUTH_NOKEY;
if (info->keysize < 0)
val |= BOLT_AUTH_NOKEY;
}
msk |= BOLT_AUTH_NOPCIE;
if (!bolt_security_allows_pcie (sl))
val |= BOLT_AUTH_NOPCIE;
msk |= BOLT_AUTH_BOOT;
if (info->boot > 0)
val |= BOLT_AUTH_BOOT;
if (mask)
*mask = msk;
return val;
}
/* device authorization */
static gboolean
device_check_parent_auth (BoltDevice *dev,
DIR *devdir,
int *auth)
{
g_autoptr(DIR) parent = NULL;
g_autoptr(GError) err = NULL;
gboolean ok;
parent = bolt_opendir_at (dirfd (devdir), "..", O_RDONLY, &err);
if (!parent)
{
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("authorize"),
"could not open parent directory of device");
return FALSE;
}
ok = bolt_read_int_at (dirfd (parent), "authorized", auth, &err);
if (!ok)
{
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("authorize"),
"could not read parent authorization");
return FALSE;
}
return TRUE;
}
static void
authorize_adjust_error (BoltDevice *dev,
DIR *devdir,
GError **error)
{
GError *err;
gint auth = -1;
gboolean ok;
if (error == NULL)
return;
err = *error;
if (bolt_err_inval (err))
{
/* EINVAL is reported by the kernel if:
* a) device is already authorized
* b) parent device is *not* authorized
*/
/* check for a) */
ok = bolt_read_int_at (dirfd (devdir), "authorized", &auth, NULL);
if (ok && auth > 0)
{
g_clear_error (error);
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_BADSTATE,
"device is already authorized");
return;
}
/* check for b) */
ok = device_check_parent_auth (dev, devdir, &auth);
if (ok && auth < 1)
{
/* parent is not authorized, adjust the error */
g_clear_error (error);
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_AUTHCHAIN,
"parent device is not authorized");
return;
}
}
/* if we have a generic, non bolt error, it is most likely a
* G_IO_ERROR. We prefix the error message to make it clearer
* where the (probably cryptic) error originated
*/
if (err->domain != BOLT_ERROR)
g_prefix_error (error, "%s", "kernel error: ");
}
typedef struct
{
BoltAuth *auth;
/* the outer callback */
GAsyncReadyCallback callback;
gpointer user_data;
} AuthData;
static void
auth_data_free (gpointer data)
{
AuthData *auth = data;
g_clear_object (&auth->auth);
g_slice_free (AuthData, auth);
}
static gboolean
authorize_device_internal (BoltDevice *dev,
BoltAuth *auth,
GError **error)
{
g_autoptr(DIR) devdir = NULL;
BoltKey *key;
BoltSecurity level;
gboolean ok;
key = bolt_auth_get_key (auth);
level = bolt_auth_get_level (auth);
devdir = bolt_opendir (dev->syspath, error);
if (devdir == NULL)
return FALSE;
ok = bolt_verify_uid (dirfd (devdir), dev->uid, error);
if (!ok)
return FALSE;
if (key)
{
int keyfd;
bolt_debug (LOG_DEV (dev), LOG_TOPIC ("authorize"), "writing key");
keyfd = bolt_openat (dirfd (devdir),
"key",
O_WRONLY | O_CLOEXEC,
0,
error);
if (keyfd < 0)
return FALSE;
ok = bolt_key_write_to (key, keyfd, &level, error);
close (keyfd);
if (!ok)
return FALSE;
}
bolt_debug (LOG_DEV (dev), LOG_TOPIC ("authorize"),
"writing authorization");
ok = bolt_write_char_at (dirfd (devdir),
"authorized",
level,
error);
if (!ok)
authorize_adjust_error (dev, devdir, error);
return ok;
}
static void
authorize_in_thread (GTask *task,
gpointer source,
gpointer context,
GCancellable *cancellable)
{
GError *error = NULL;
BoltDevice *dev = source;
AuthData *auth_data = context;
BoltAuth *auth = auth_data->auth;
gboolean ok;
ok = authorize_device_internal (dev, auth, &error);
if (!ok)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
}
static void
authorize_thread_done (GObject *object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
BoltDevice *dev = BOLT_DEVICE (object);
GTask *task = G_TASK (res);
BoltStatus status;
AuthData *auth_data;
BoltAuth *auth;
guint aflags;
guint mask;
gboolean chg;
gboolean ok;
guint64 now;
auth_data = g_task_get_task_data (task);
auth = auth_data->auth;
ok = g_task_propagate_boolean (task, &error);
if (!ok)
bolt_auth_return_error (auth, &error);
now = bolt_now_in_seconds ();
status = bolt_auth_to_status (auth);
aflags = bolt_auth_to_flags (auth, &mask);
bolt_info (LOG_DEV (dev), LOG_TOPIC ("authorize"),
"finished: %s (status: %s, flags: %u)",
ok ? "ok" : "FAIL",
bolt_status_to_string (status),
aflags);
g_object_freeze_notify (object);
dev->authtime = now;
g_object_notify_by_pspec (G_OBJECT (dev), props[PROP_AUTHTIME]);
chg = bolt_flags_update (aflags, &dev->aflags, mask);
if (chg)
g_object_notify_by_pspec (object, props[PROP_AUTHFLAGS]);
device_set_status_internal (dev, status, TRUE);
g_object_thaw_notify (object);
if (dev->store)
bolt_store_put_times (dev->store, dev->uid, NULL,
"authtime", now,
NULL);
if (auth_data->callback)
auth_data->callback (G_OBJECT (dev),
G_ASYNC_RESULT (auth),
auth_data->user_data);
}
static GTask *
authorize_prepare (BoltDevice *dev,
BoltAuth *auth,
GAsyncReadyCallback callback,
gpointer user_data)
{
BoltSecurity lvl;
AuthData *auth_data;
GTask *task;
g_object_set (auth, "device", dev, NULL);
if (!bolt_status_is_pending (dev->status))
{
bolt_auth_return_new_error (auth, BOLT_ERROR, BOLT_ERROR_BADSTATE,
"wrong device state: %s",
bolt_status_to_string (dev->status));
if (callback)
callback (G_OBJECT (dev), G_ASYNC_RESULT (auth), user_data);
return NULL;
}
task = g_task_new (dev, NULL, authorize_thread_done, NULL);
auth_data = g_slice_new (AuthData);
auth_data->callback = callback;
auth_data->user_data = user_data;
auth_data->auth = g_object_ref (auth);
g_task_set_task_data (task, auth_data, auth_data_free);
g_object_set (dev, "status", BOLT_STATUS_AUTHORIZING, NULL);
lvl = bolt_auth_get_level (auth);
bolt_info (LOG_DEV (dev), LOG_TOPIC ("authorize"),
"authorization prepared for '%s' level",
bolt_security_to_string (lvl));
return task;
}
static gboolean
authorize_device_idle (gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_val_if_fail (G_IS_TASK (user_data), G_SOURCE_REMOVE);
task = (GTask *) user_data;
g_task_run_in_thread (task, authorize_in_thread);
return G_SOURCE_REMOVE;
}
/* dbus property setter */
static gboolean
handle_set_label (BoltExported *obj,
const char *name,
const GValue *value,
GError **error)
{
g_autofree char *nick = NULL;
g_autofree char *old = NULL;
BoltDevice *dev = BOLT_DEVICE (obj);
const char *str = g_value_get_string (value);
gboolean ok;
nick = bolt_strdup_validate (str);
if (nick == NULL)
{
g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"string is invalid");
return FALSE;
}
else if (strlen (nick) > 255)
{
g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"string is too long");
return FALSE;
}
if (dev->store == NULL)
{
g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"device is not stored");
return FALSE;
}
old = dev->label;
dev->label = g_steal_pointer (&nick);
ok = bolt_store_put_device (dev->store, dev, dev->policy, NULL, error);
if (!ok)
{
bolt_warn_err (*error, LOG_DEV (dev), "failed to store device");
nick = dev->label;
dev->label = g_steal_pointer (&old);
}
return ok;
}
static gboolean
handle_set_policy (BoltExported *obj,
const char *name,
const GValue *value,
GError **error)
{
BoltDevice *dev = BOLT_DEVICE (obj);
BoltPolicy before = dev->policy;
BoltPolicy policy = g_value_get_enum (value);
gboolean ok;
if (policy == BOLT_POLICY_UNKNOWN ||
policy == BOLT_POLICY_DEFAULT)
{
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"invalid policy (%d)", policy);
return FALSE;
}
else if (dev->store == NULL)
{
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"device is not stored");
return FALSE;
}
if (policy == dev->policy)
return TRUE;
ok = bolt_store_put_device (dev->store, dev, policy, NULL, error);
if (!ok)
{
bolt_warn_err (*error, LOG_DEV (dev), "failed to store device");
dev->policy = before;
}
if (policy == BOLT_POLICY_AUTO)
bolt_domain_foreach (dev->domain, bolt_bootacl_add, dev);
else if (policy == BOLT_POLICY_MANUAL)
bolt_domain_foreach (dev->domain, bolt_bootacl_del, dev);
return ok;
}
/* dbus methods */
static void
handle_authorize_done (GObject *device,
GAsyncResult *res,
gpointer user_data)
{
BoltDevice *dev = BOLT_DEVICE (device);
GDBusMethodInvocation *inv;
BoltKeyState ks;
GError *error = NULL;
BoltAuth *auth;
gboolean ok;
inv = user_data;
auth = BOLT_AUTH (res);
ok = bolt_auth_check (auth, &error);
if (!ok)
{
g_dbus_method_invocation_take_error (inv, error);
return;
}
ks = bolt_auth_get_keystate (auth);
if (ks == BOLT_KEY_NEW)
{
g_autoptr(GError) err = NULL;
BoltKey *key = bolt_auth_get_key (auth);
ok = bolt_store_put_key (dev->store, dev->uid, key, &err);
if (!ok)
bolt_warn_err (err, "failed to store key");
else
g_object_set (dev, "key", ks, NULL);
}
g_dbus_method_invocation_return_value (inv, g_variant_new ("()"));
}
static gboolean
device_should_upgrade_key (BoltDevice *dev)
{
gboolean upgrade = FALSE;
const char *reason = NULL;
if (dev->store == NULL)
reason = " (device not stored)";
else if (bolt_flag_isset (dev->aflags, BOLT_AUTH_NOKEY))
reason = " (device cannot use keys)";
else
upgrade = TRUE;
bolt_msg (LOG_DEV (dev), "performing key upgrade: %s%s",
bolt_yesno (upgrade), reason ? : "");
return upgrade;
}
static GVariant *
handle_authorize (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *inv,
GError **error)
{
g_autoptr(BoltAuth) auth = NULL;
g_autoptr(BoltKey) key = NULL;
BoltDevice *dev = BOLT_DEVICE (object);
BoltSecurity level;
/* In bolt_device_authorize the state is also checked, but it
* is done already here to fail quicker and avoid accessing the
* potentially unset domain (if e.g. the device is not connected).
*/
if (!bolt_status_is_pending (dev->status))
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_BADSTATE,
"wrong device state: %s",
bolt_status_to_string (dev->status));
return NULL;
}
else if (dev->domain == NULL)
{
bolt_bug (LOG_DEV (dev), "device connected but no domain");
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_BADSTATE,
"device has no domain associated");
return NULL;
}
level = bolt_domain_get_security (dev->domain);
if (level == BOLT_SECURITY_SECURE)
{
if (dev->key)
key = bolt_store_get_key (dev->store, dev->uid, error);
else if (device_should_upgrade_key (dev))
key = bolt_key_new (error);
else
level = BOLT_SECURITY_USER;
}
/* happens if the key could not be read (fatal error) or if a new
* key could not be generated (should practically never happen).
* In both cases 'error' will be set. */
if (level == BOLT_SECURITY_SECURE && key == NULL)
return NULL;
auth = bolt_auth_new (dev, level, key);
bolt_device_authorize (dev, auth, handle_authorize_done, inv);
return NULL;
}
/* public methods */
BoltDevice *
bolt_device_new_for_udev (struct udev_device *udev,
BoltDomain *domain,
GError **error)
{
g_auto(BoltIdent) id = BOLT_IDENT_INIT;
const char *uid;
BoltDevInfo info;
BoltStatus status;
BoltAuthFlags aflags;
BoltDeviceType type;
BoltDevice *dev;
gboolean ok;
guint64 ct, at;
g_return_val_if_fail (udev != NULL, NULL);
g_return_val_if_fail (domain != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
uid = bolt_sysfs_device_get_unique_id (udev, error);
if (uid == NULL)
return NULL;
ok = bolt_sysfs_info_for_device (udev, TRUE, &info, error);
if (!ok)
return NULL;
if (info.parent == NULL)
type = BOLT_DEVICE_HOST;
else
type = BOLT_DEVICE_PERIPHERAL;
if (type == BOLT_DEVICE_HOST)
ok = bolt_sysfs_host_ident (udev, &id, error);
else
ok = bolt_sysfs_device_ident (udev, &id, error);
if (!ok)
return NULL;
ct = (guint64) info.ctim;
status = bolt_status_from_info (&info);
aflags = bolt_auth_flags_from_info (&info, domain, NULL);
at = bolt_status_is_authorized (status) ? ct : 0;
dev = g_object_new (BOLT_TYPE_DEVICE,
"uid", uid,
"name", id.name,
"vendor", id.vendor,
"type", type,
"generation", info.generation,
"status", status,
"authflags", aflags,
"sysfs-path", info.syspath,
"domain", domain,
"parent", info.parent,
"conntime", ct,
"authtime", at,
"linkspeed", &info.linkspeed,
NULL);
return dev;
}
const char *
bolt_device_export (BoltDevice *device,
GDBusConnection *connection,
GError **error)
{
gboolean ok;
g_return_val_if_fail (BOLT_IS_DEVICE (device), NULL);
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
ok = bolt_exported_export (BOLT_EXPORTED (device),
connection,
NULL,
error);
return ok ? bolt_device_get_object_path (device) : NULL;
}
void
bolt_device_unexport (BoltDevice *device)
{
bolt_exported_unexport (BOLT_EXPORTED (device));
}
void
bolt_device_authorize (BoltDevice *dev,
BoltAuth *auth,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (BOLT_IS_DEVICE (dev));
g_return_if_fail (BOLT_IS_AUTH (auth));
task = authorize_prepare (dev, auth, callback, user_data);
if (task == NULL)
return;
g_task_run_in_thread (task, authorize_in_thread);
}
void
bolt_device_authorize_idle (BoltDevice *dev,
BoltAuth *auth,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task = NULL;
g_return_if_fail (BOLT_IS_DEVICE (dev));
g_return_if_fail (BOLT_IS_AUTH (auth));
task = authorize_prepare (dev, auth, callback, user_data);
if (task == NULL)
return;
g_idle_add (authorize_device_idle, task);
}
BoltStatus
bolt_device_connected (BoltDevice *dev,
BoltDomain *domain,
struct udev_device *udev)
{
g_autoptr(GError) err = NULL;
BoltAuthFlags aflags;
BoltDevInfo info;
BoltStatus status;
gboolean change;
gboolean ok;
guint64 ct, at;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), BOLT_STATUS_UNKNOWN);
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), BOLT_STATUS_UNKNOWN);
ok = bolt_sysfs_info_for_device (udev, TRUE, &info, &err);
if (!ok)
{
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("udev"),
"failed to get device info");
g_clear_error (&err);
}
status = bolt_status_from_info (&info);
aflags = bolt_auth_flags_from_info (&info, domain, NULL);
ct = (guint64) info.ctim;
at = bolt_status_is_authorized (status) ? ct : 0;
change = info.generation != dev->gen;
g_object_set (G_OBJECT (dev),
"generation", info.generation,
"parent", info.parent,
"sysfs-path", info.syspath,
"domain", domain,
"status", status,
"authflags", aflags,
"conntime", ct,
"authtime", at,
"linkspeed", &info.linkspeed,
NULL);
bolt_info (LOG_DEV (dev), "parent is %.13s...", dev->parent);
if (change && dev->store)
{
bolt_info (LOG_DEV (dev), LOG_TOPIC ("store"),
"updating device");
ok = bolt_store_put_device (dev->store,
dev,
BOLT_POLICY_DEFAULT,
NULL,
&err);
if (!ok)
{
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("store"),
"failed to update device");
g_clear_error (&err);
}
}
bolt_store_put_times (dev->store, dev->uid, NULL,
"conntime", ct,
"authtime", at,
NULL);
return status;
}
BoltStatus
bolt_device_disconnected (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), BOLT_STATUS_UNKNOWN);
g_object_set (G_OBJECT (dev),
"parent", NULL,
"sysfs-path", NULL,
"domain", NULL,
"status", BOLT_STATUS_DISCONNECTED,
NULL);
/* check if we have a new key for the device, and
* if so, change its state to KEY_HAVE, because
* now it is not new anymore.
*/
if (dev->key == BOLT_KEY_NEW)
g_object_set (G_OBJECT (dev), "key", BOLT_KEY_HAVE, NULL);
return dev->status;
}
gboolean
bolt_device_is_connected (BoltDevice *device)
{
g_return_val_if_fail (BOLT_IS_DEVICE (device), FALSE);
return bolt_status_is_connected (device->status);
}
gboolean
bolt_device_is_authorized (BoltDevice *device)
{
g_return_val_if_fail (BOLT_IS_DEVICE (device), FALSE);
return bolt_status_is_authorized (device->status);
}
BoltStatus
bolt_device_update_from_udev (BoltDevice *dev,
struct udev_device *udev)
{
g_autoptr(GError) err = NULL;
BoltLinkSpeed linkspeed;
BoltAuthFlags aflags;
BoltDevInfo info;
BoltStatus status;
guint mask;
gboolean chg;
gboolean ok;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), BOLT_STATUS_UNKNOWN);
g_return_val_if_fail (udev != NULL, BOLT_STATUS_UNKNOWN);
/* if we are currently authorizing, let's not update
* the status, because we are most likely causing that
* udev update and we cannot determine AUTHORIZING from
* outside;
* The status will be set by authorize_thread_done()
*/
if (dev->status == BOLT_STATUS_AUTHORIZING)
return dev->status;
ok = bolt_sysfs_info_for_device (udev, FALSE, &info, &err);
if (!ok)
{
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("udev"),
"failed to get device info");
return dev->status;
}
status = bolt_status_from_info (&info);
aflags = bolt_auth_flags_from_info (&info, dev->domain, &mask);
g_object_freeze_notify (G_OBJECT (dev));
if (bolt_status_is_authorized (status) &&
!bolt_status_is_authorized (dev->status))
{
dev->authtime = bolt_now_in_seconds ();
g_object_notify_by_pspec (G_OBJECT (dev), props[PROP_AUTHTIME]);
bolt_store_put_times (dev->store, dev->uid, NULL,
"authtime", dev->authtime,
NULL);
}
chg = bolt_flags_update (aflags, &dev->aflags, mask);
if (chg)
g_object_notify_by_pspec (G_OBJECT (dev), props[PROP_AUTHFLAGS]);
device_set_status_internal (dev, status, TRUE);
bolt_sysfs_read_link_speed (udev, &linkspeed);
if (!bolt_link_speed_equal (&dev->linkspeed, &linkspeed))
{
dev->linkspeed = linkspeed;
g_object_notify_by_pspec (G_OBJECT (dev), props[PROP_LINKSPEED]);
}
g_object_thaw_notify (G_OBJECT (dev));
return status;
}
BoltDomain *
bolt_device_get_domain (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
return dev->domain;
}
BoltKeyState
bolt_device_get_keystate (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), BOLT_KEY_MISSING);
return dev->key;
}
const char *
bolt_device_get_name (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
return dev->name;
}
const char *
bolt_device_get_object_path (BoltDevice *device)
{
g_return_val_if_fail (BOLT_IS_DEVICE (device), NULL);
if (!bolt_exported_is_exported (BOLT_EXPORTED (device)))
return NULL;
return bolt_exported_get_object_path (BOLT_EXPORTED (device));
}
BoltPolicy
bolt_device_get_policy (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), BOLT_POLICY_UNKNOWN);
return dev->policy;
}
const char *
bolt_device_get_uid (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
return dev->uid;
}
BoltSecurity
bolt_device_get_security (BoltDevice *dev)
{
g_return_val_if_fail (dev != NULL, BOLT_SECURITY_UNKNOWN);
g_return_val_if_fail (dev->domain != NULL, BOLT_SECURITY_UNKNOWN);
return bolt_domain_get_security (dev->domain);
}
BoltStatus
bolt_device_get_status (BoltDevice *dev)
{
g_return_val_if_fail (dev != NULL, BOLT_STATUS_UNKNOWN);
return dev->status;
}
BoltAuthFlags
bolt_device_get_authflags (BoltDevice *dev)
{
g_return_val_if_fail (dev != NULL, BOLT_AUTH_NONE);
return dev->aflags;
}
gboolean
bolt_device_get_stored (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
return dev->store != NULL;
}
gboolean
bolt_device_has_iommu (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
if (dev->domain == NULL)
return FALSE;
return bolt_domain_has_iommu (dev->domain);
}
gboolean
bolt_device_has_key (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
return !(dev->key == BOLT_KEY_UNKNOWN ||
dev->key == BOLT_KEY_MISSING);
}
const char *
bolt_device_get_syspath (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
return dev->syspath;
}
const char *
bolt_device_get_vendor (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
return dev->vendor;
}
guint
bolt_device_get_generation (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), 0);
return dev->gen;
}
BoltDeviceType
bolt_device_get_device_type (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), BOLT_DEVICE_UNKNOWN_TYPE);
return dev->type;
}
gboolean
bolt_device_is_host (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), BOLT_DEVICE_UNKNOWN_TYPE);
return dev->type == BOLT_DEVICE_HOST;
}
const char *
bolt_device_get_label (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
return dev->label;
}
guint64
bolt_device_get_authtime (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), 0);
return dev->authtime;
}
guint64
bolt_device_get_conntime (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), 0);
return dev->conntime;
}
guint64
bolt_device_get_storetime (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), 0);
return dev->storetime;
}
gboolean
bolt_device_supports_secure_mode (BoltDevice *dev)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
return bolt_flag_isclear (dev->aflags, BOLT_AUTH_NOKEY);
}
gboolean
bolt_device_check_authflag (BoltDevice *dev,
BoltAuthFlags flag)
{
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
return bolt_flag_isset (dev->aflags, flag);
}
gboolean
bolt_device_get_key_from_sysfs (BoltDevice *dev,
BoltKey **key,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(GFile) keyfile = NULL;
g_autofree char *keypath = NULL;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
g_return_val_if_fail (key != NULL && *key == NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (dev->syspath == NULL)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_BADSTATE,
"device is not connected");
return FALSE;
}
keypath = g_build_filename (dev->syspath, "key", NULL);
keyfile = g_file_new_for_path (keypath);
*key = bolt_key_load_file (keyfile, &err);
if (*key != NULL)
return TRUE;
else if (bolt_err_notfound (err) || bolt_err_nokey (err))
return TRUE;
else
return bolt_error_propagate (error, &err);
}
gboolean
bolt_device_load_key (BoltDevice *dev,
BoltKey **key,
GError **error)
{
BoltKey *k;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
g_return_val_if_fail (key != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (!bolt_device_has_key (dev) || dev->store == NULL)
{
*key = NULL;
return TRUE;
}
k = bolt_store_get_key (dev->store, dev->uid, error);
if (k == NULL)
return FALSE;
*key = k;
return TRUE;
}
/* bolt_domain_foreach helpers */
void
bolt_bootacl_add (gpointer domain,
gpointer device)
{
g_autoptr(GError) err = NULL;
BoltDomain *dom = BOLT_DOMAIN (domain);
BoltDevice *dev = BOLT_DEVICE (device);
const char *uid = bolt_device_get_uid (dev);
gboolean ok;
bolt_info (LOG_TOPIC ("bootacl"),
LOG_DOM (dom), LOG_DEV (dev),
"adding %.17s... ", uid);
if (!bolt_domain_supports_bootacl (dom))
return;
if (bolt_domain_bootacl_contains (dom, uid))
return;
ok = bolt_domain_bootacl_add (dom, uid, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("bootacl"),
LOG_DOM (dom), LOG_DEV_UID (uid),
"could not add device");
}
}
void
bolt_bootacl_del (gpointer domain,
gpointer device)
{
g_autoptr(GError) err = NULL;
BoltDomain *dom = BOLT_DOMAIN (domain);
BoltDevice *dev = BOLT_DEVICE (device);
const char *uid = bolt_device_get_uid (dev);
gboolean ok;
bolt_info (LOG_TOPIC ("bootacl"),
LOG_DOM (dom), LOG_DEV (dev),
"removing %.17s...", uid);
if (!bolt_domain_supports_bootacl (dom))
return;
if (!bolt_domain_bootacl_contains (dom, uid))
return;
ok = bolt_domain_bootacl_del (dom, uid, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("bootacl"),
LOG_DEV_UID (uid), LOG_DOM (dom),
"could not remove device");
}
}
bolt-0.9.2/boltd/bolt-device.h 0000664 0000000 0000000 00000011346 14174530510 0016141 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-auth.h"
#include "bolt-enums.h"
#include "bolt-exported.h"
/* forward declaration */
struct udev_device;
typedef struct _BoltDomain BoltDomain;
G_BEGIN_DECLS
#define BOLT_TYPE_DEVICE bolt_device_get_type ()
G_DECLARE_FINAL_TYPE (BoltDevice, bolt_device, BOLT, DEVICE, BoltExported);
BoltDevice * bolt_device_new_for_udev (struct udev_device *udev,
BoltDomain *domain,
GError **error);
const char * bolt_device_export (BoltDevice *device,
GDBusConnection *connection,
GError **error);
void bolt_device_unexport (BoltDevice *device);
BoltStatus bolt_device_connected (BoltDevice *dev,
BoltDomain *domain,
struct udev_device *udev);
BoltStatus bolt_device_disconnected (BoltDevice *dev);
gboolean bolt_device_is_connected (BoltDevice *device);
gboolean bolt_device_is_authorized (BoltDevice *device);
BoltStatus bolt_device_update_from_udev (BoltDevice *dev,
struct udev_device *udev);
void bolt_device_authorize (BoltDevice *dev,
BoltAuth *auth,
GAsyncReadyCallback callback,
gpointer user_data);
void bolt_device_authorize_idle (BoltDevice *dev,
BoltAuth *auth,
GAsyncReadyCallback callback,
gpointer user_data);
BoltDomain * bolt_device_get_domain (BoltDevice *dev);
BoltKeyState bolt_device_get_keystate (BoltDevice *dev);
const char * bolt_device_get_name (BoltDevice *dev);
const char * bolt_device_get_object_path (BoltDevice *device);
BoltPolicy bolt_device_get_policy (BoltDevice *dev);
const char * bolt_device_get_uid (BoltDevice *dev);
BoltSecurity bolt_device_get_security (BoltDevice *dev);
gboolean bolt_device_get_stored (BoltDevice *dev);
BoltStatus bolt_device_get_status (BoltDevice *dev);
BoltAuthFlags bolt_device_get_authflags (BoltDevice *dev);
const char * bolt_device_get_syspath (BoltDevice *dev);
const char * bolt_device_get_vendor (BoltDevice *dev);
BoltDeviceType bolt_device_get_device_type (BoltDevice *dev);
gboolean bolt_device_is_host (BoltDevice *dev);
const char * bolt_device_get_label (BoltDevice *dev);
guint64 bolt_device_get_authtime (BoltDevice *dev);
guint64 bolt_device_get_conntime (BoltDevice *dev);
guint64 bolt_device_get_storetime (BoltDevice *dev);
guint bolt_device_get_generation (BoltDevice *dev);
gboolean bolt_device_has_iommu (BoltDevice *dev);
gboolean bolt_device_has_key (BoltDevice *dev);
gboolean bolt_device_supports_secure_mode (BoltDevice *dev);
gboolean bolt_device_check_authflag (BoltDevice *dev,
BoltAuthFlags flag);
gboolean bolt_device_get_key_from_sysfs (BoltDevice *dev,
BoltKey **key,
GError **error);
gboolean bolt_device_load_key (BoltDevice *dev,
BoltKey **key,
GError **error);
/* bolt_domain_foreach helpers */
void bolt_bootacl_add (gpointer domain,
gpointer device);
void bolt_bootacl_del (gpointer domain,
gpointer device);
G_END_DECLS
bolt-0.9.2/boltd/bolt-domain.c 0000664 0000000 0000000 00000077702 14174530510 0016154 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-error.h"
#include "bolt-glue.h"
#include "bolt-log.h"
#include "bolt-str.h"
#include "bolt-store.h"
#include "bolt-sysfs.h"
#include "bolt-list.h"
#include "bolt-domain.h"
#include
#include
static void bolt_domain_store_setter (BoltDomain *domain,
const GValue *val);
static void bolt_domain_bootacl_open_log (BoltDomain *domain);
static void bolt_domain_bootacl_remove_log (BoltDomain *domain);
/* dbus property setter */
static gboolean handle_set_bootacl (BoltExported *obj,
const char *name,
const GValue *value,
GError **error);
struct _BoltDomain
{
BoltExported object;
/* internal list */
BoltList domains;
gint sort;
BoltStore *store;
BoltJournal *acllog;
/* persistent */
char *uid;
/* sysfs */
char *id;
char *syspath;
BoltSecurity security;
GStrv bootacl;
gboolean iommu;
};
enum {
PROP_0,
/* internal properties */
PROP_STORE,
PROP_OBJECT_ID,
/* exported properties */
PROP_UID,
PROP_ID,
PROP_SYSPATH,
PROP_SECURITY,
PROP_BOOTACL,
PROP_IOMMU,
PROP_LAST,
PROP_EXPORTED = PROP_UID
};
static GParamSpec *props[PROP_LAST] = { NULL, };
enum {
SIGNAL_BOOTACL_CHANGED,
SIGNAL_BOOTACL_ALLOC,
SIGNAL_LAST
};
static guint signals[SIGNAL_LAST] = {0, };
G_DEFINE_TYPE (BoltDomain,
bolt_domain,
BOLT_TYPE_EXPORTED)
static void
bolt_domain_finalize (GObject *object)
{
BoltDomain *dom = BOLT_DOMAIN (object);
g_clear_object (&dom->store);
g_clear_object (&dom->acllog);
g_free (dom->uid);
g_free (dom->id);
g_free (dom->syspath);
g_strfreev (dom->bootacl);
G_OBJECT_CLASS (bolt_domain_parent_class)->finalize (object);
}
static void
bolt_domain_init (BoltDomain *dom)
{
bolt_list_init (&dom->domains);
}
static void
bolt_domain_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltDomain *dom = BOLT_DOMAIN (object);
switch (prop_id)
{
case PROP_STORE:
g_value_set_object (value, dom->store);
break;
case PROP_OBJECT_ID:
case PROP_UID:
g_value_set_string (value, dom->uid);
break;
case PROP_ID:
g_value_set_string (value, dom->id);
break;
case PROP_SYSPATH:
g_value_set_string (value, dom->syspath);
break;
case PROP_SECURITY:
g_value_set_enum (value, dom->security);
break;
case PROP_BOOTACL:
g_value_set_boxed (value, dom->bootacl);
break;
case PROP_IOMMU:
g_value_set_boolean (value, dom->iommu);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_domain_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltDomain *dom = BOLT_DOMAIN (object);
switch (prop_id)
{
case PROP_STORE:
bolt_domain_store_setter (dom, value);
break;
case PROP_UID:
dom->uid = g_value_dup_string (value);
break;
case PROP_ID:
dom->id = g_value_dup_string (value);
break;
case PROP_SYSPATH:
g_clear_pointer (&dom->syspath, g_free);
dom->syspath = g_value_dup_string (value);
break;
case PROP_SECURITY:
dom->security = g_value_get_enum (value);
break;
case PROP_BOOTACL:
g_strfreev (dom->bootacl);
dom->bootacl = g_value_dup_boxed (value);
break;
case PROP_IOMMU:
dom->iommu = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_domain_class_init (BoltDomainClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
BoltExportedClass *exported_class = BOLT_EXPORTED_CLASS (klass);
gobject_class->finalize = bolt_domain_finalize;
gobject_class->get_property = bolt_domain_get_property;
gobject_class->set_property = bolt_domain_set_property;
props[PROP_STORE] =
g_param_spec_object ("store",
NULL, NULL,
BOLT_TYPE_STORE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_OBJECT_ID] =
bolt_param_spec_override (gobject_class, "object-id");
props[PROP_UID] =
g_param_spec_string ("uid",
"Uid", NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_ID] =
g_param_spec_string ("id",
"Id", NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_SYSPATH] =
g_param_spec_string ("syspath",
"SysfsPath", NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_SECURITY] =
g_param_spec_enum ("security",
"SecurityLevel", NULL,
BOLT_TYPE_SECURITY,
BOLT_SECURITY_UNKNOWN,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_BOOTACL] =
g_param_spec_boxed ("bootacl",
"BootACL", NULL,
G_TYPE_STRV,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_IOMMU] =
g_param_spec_boolean ("iommu",
"IOMMU", NULL,
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
bolt_exported_class_set_interface_info (exported_class,
BOLT_DBUS_DOMAIN_INTERFACE,
BOLT_DBUS_GRESOURCE_PATH);
bolt_exported_class_set_object_path (exported_class, BOLT_DBUS_PATH_DOMAINS);
bolt_exported_class_export_properties (exported_class,
PROP_EXPORTED,
PROP_LAST,
props);
bolt_exported_class_property_setter (exported_class,
props[PROP_BOOTACL],
handle_set_bootacl);
signals[SIGNAL_BOOTACL_CHANGED] =
g_signal_new ("bootacl-changed",
BOLT_TYPE_DOMAIN,
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
2,
G_TYPE_BOOLEAN,
G_TYPE_HASH_TABLE);
signals[SIGNAL_BOOTACL_ALLOC] =
g_signal_new ("bootacl-alloc",
BOLT_TYPE_DOMAIN,
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_true_handled,
NULL,
NULL,
G_TYPE_BOOLEAN,
3,
G_TYPE_STRV,
G_TYPE_STRING,
G_TYPE_POINTER);
}
/* */
static void
bolt_domain_store_setter (BoltDomain *domain,
const GValue *value)
{
BoltStore *store;
store = g_value_get_object (value);
if (domain->store == store)
return;
if (domain->store)
{
bolt_domain_bootacl_remove_log (domain);
g_clear_object (&domain->store);
}
if (store != NULL)
{
domain->store = g_object_ref (store);
bolt_domain_bootacl_open_log (domain);
}
}
static void
bolt_domain_bootacl_open_log (BoltDomain *domain)
{
g_autoptr(GError) err = NULL;
BoltJournal *log = NULL;
const char *uid = domain->uid;
log = bolt_store_open_journal (domain->store,
"bootacl",
uid,
&err);
if (log == NULL)
bolt_warn_err (err, LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"could not open journal");
domain->acllog = log;
}
static void
bolt_domain_bootacl_remove_log (BoltDomain *domain)
{
g_autoptr(GError) err = NULL;
gboolean ok;
if (domain->acllog == NULL)
return;
g_clear_object (&domain->acllog);
g_return_if_fail (domain->store != NULL);
bolt_info (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"removing journal");
ok = bolt_store_del_journal (domain->store,
"bootacl",
domain->uid,
&err);
if (!ok)
bolt_warn_err (err, LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"could not remove journal");
}
static gboolean
bolt_domain_bootacl_can_update (BoltDomain *domain,
GError **error)
{
if (!bolt_domain_supports_bootacl (domain))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"boot ACL not supported on domain '%s'", domain->uid);
return FALSE;
}
if (domain->syspath == NULL && domain->acllog == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"domain offline and no bootacl journal");
return FALSE;
}
return TRUE;
}
static void
bolt_domain_bootacl_update (BoltDomain *domain,
GStrv *acl,
GHashTable *diff_hint)
{
g_autoptr(GHashTable) diff = NULL;
gboolean pending;
gboolean same;
guint signal;
g_return_if_fail (domain != NULL);
g_return_if_fail (acl != NULL);
same = bolt_strv_equal (domain->bootacl, *acl);
if (same)
{
bolt_debug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"acl unchanged, not updating");
return;
}
bolt_swap (domain->bootacl, *acl);
g_object_notify_by_pspec (G_OBJECT (domain), props[PROP_BOOTACL]);
if (domain->store)
{
g_autoptr(GError) err = NULL;
gboolean ok;
ok = bolt_store_put_domain (domain->store, domain, &err);
if (!ok)
bolt_warn_err (err, LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"could not update domain");
}
signal = signals[SIGNAL_BOOTACL_CHANGED];
pending = g_signal_has_handler_pending (domain, signal, 0, FALSE);
if (!pending)
return;
if (diff_hint != NULL)
diff = g_hash_table_ref (diff_hint);
else
diff = bolt_strv_diff (*acl, domain->bootacl);
g_signal_emit (domain, signals[SIGNAL_BOOTACL_CHANGED], 0,
g_hash_table_size (diff) > 0, diff);
}
static gboolean
bolt_domain_bootacl_remove (BoltDomain *domain,
GStrv acl,
const char *uuid,
GError **error)
{
char **target = NULL;
g_return_val_if_fail (acl != NULL, FALSE);
target = bolt_strv_contains (acl, uuid);
if (target == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"device '%s' not in boot ACL of domain '%s'",
uuid, domain->id);
return FALSE;
}
bolt_debug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"removing '%s' from bootacl", uuid);
bolt_set_strdup (target, "");
return TRUE;
}
static void
bolt_domain_bootacl_sync (BoltDomain *domain,
GStrv *sysacl)
{
g_autoptr(GError) err = NULL;
g_autoptr(GPtrArray) diff = NULL;
g_auto(GStrv) acl = NULL;
BoltJournal *log = domain->acllog;
gboolean ok;
if (bolt_strv_isempty (sysacl) || log == NULL)
return;
bolt_info (LOG_TOPIC ("bootacl"), LOG_DOM (domain), "synchronizing journal");
acl = g_strdupv (*sysacl);
diff = bolt_journal_list (log, &err);
if (diff == NULL)
{
bolt_warn_err (err, LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"could not list bootacl changes");
return;
}
bolt_debug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"journal contains %u entries", diff->len);
for (guint i = 0; i < diff->len; i++)
{
BoltJournalItem *item = g_ptr_array_index (diff, i);
BoltJournalOp op = item->op;
const char *uid = item->id;
ok = TRUE;
bolt_debug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"applying op '%c' for '%s'", op, uid);
switch (op)
{
case BOLT_JOURNAL_ADDED:
if (bolt_strv_contains (acl, uid))
{
bolt_debug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"'%s' already in acl", uid);
continue;
}
bolt_domain_bootacl_allocate (domain, acl, uid);
break;
case BOLT_JOURNAL_REMOVED:
if (!bolt_strv_contains (acl, uid))
{
bolt_debug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"'%s' already removed from acl", uid);
continue;
}
ok = bolt_domain_bootacl_remove (domain, acl, uid, &err);
break;
default:
bolt_bug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"handled journal op %d", item->op);
break;
}
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("bootacl"), LOG_DOM (domain),
LOG_DEV_UID (uid),
"applying journal op (%d) failed for %.17s",
item->op, uid);
g_clear_error (&err);
}
}
ok = bolt_journal_reset (log, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"could not reset journal");
g_clear_error (&err);
/* keep going */
}
ok = bolt_sysfs_write_boot_acl (domain->syspath, acl, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"could not write changed bootacl to sysfs");
return;
}
/* all good, we replace the passed in one with our version */
bolt_swap (acl, *sysacl);
}
/* */
BoltDomain *
bolt_domain_new_for_udev (struct udev_device *udev,
const char *uid,
GError **error)
{
g_autoptr(GError) err = NULL;
g_auto(GStrv) acl = NULL;
BoltDomain *dom = NULL;
BoltSecurity security = BOLT_SECURITY_UNKNOWN;
const char *syspath;
const char *sysname;
gboolean iommu;
gboolean ok;
gint sort = -1;
g_return_val_if_fail (udev != NULL, NULL);
g_return_val_if_fail (uid != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (!bolt_sysfs_device_is_domain (udev, error))
return NULL;
syspath = udev_device_get_syspath (udev);
sysname = udev_device_get_sysname (udev);
if (sysname == NULL)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"could not get domain id from udev");
return NULL;
}
if (g_str_has_prefix (sysname, "domain"))
{
const char *ptr = sysname + strlen ("domain");
bolt_str_parse_as_int (ptr, &sort, NULL);
}
security = bolt_sysfs_security_for_device (udev, error);
if (security == BOLT_SECURITY_UNKNOWN)
return NULL;
ok = bolt_sysfs_read_boot_acl (udev, &acl, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("udev"),
"failed to read boot_acl");
g_clear_error (&err);
}
ok = bolt_sysfs_read_iommu (udev, &iommu, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("udev"),
"failed to read iommu");
g_clear_error (&err);
}
dom = g_object_new (BOLT_TYPE_DOMAIN,
"uid", uid,
"id", sysname,
"syspath", syspath,
"security", security,
"bootacl", acl,
"iommu", iommu,
NULL);
return dom;
}
const char *
bolt_domain_get_uid (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
return domain->uid;
}
const char *
bolt_domain_get_id (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
return domain->id;
}
const char *
bolt_domain_get_syspath (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
return domain->syspath;
}
BoltSecurity
bolt_domain_get_security (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), BOLT_SECURITY_UNKNOWN);
return domain->security;
}
GStrv
bolt_domain_get_bootacl (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
return domain->bootacl;
}
GStrv
bolt_domain_dup_bootacl (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
return g_strdupv (domain->bootacl);
}
gboolean
bolt_domain_is_stored (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
return domain->store != NULL;
}
gboolean
bolt_domain_is_connected (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
return domain->syspath != NULL;
}
gboolean
bolt_domain_has_iommu (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
return domain->iommu;
}
void
bolt_domain_export (BoltDomain *domain,
GDBusConnection *bus)
{
g_autoptr(GError) err = NULL;
BoltExported *exported;
const char *opath;
gboolean ok;
g_return_if_fail (BOLT_IS_DOMAIN (domain));
g_return_if_fail (G_IS_DBUS_CONNECTION (bus));
exported = BOLT_EXPORTED (domain);
ok = bolt_exported_export (exported, bus, NULL, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("dbus"),
"error exporting a domain");
return;
}
opath = bolt_exported_get_object_path (exported);
bolt_info (LOG_TOPIC ("dbus"), LOG_DOM (domain),
"exported domain at %s", opath);
}
void
bolt_domain_connected (BoltDomain *domain,
struct udev_device *dev)
{
g_autoptr(GError) err = NULL;
g_auto(GStrv) acl = NULL;
BoltSecurity security;
const char *syspath;
const char *id;
gboolean iommu;
gboolean ok;
g_return_if_fail (BOLT_IS_DOMAIN (domain));
g_return_if_fail (dev != NULL);
id = udev_device_get_sysname (dev);
syspath = udev_device_get_syspath (dev);
if (domain->syspath != NULL && !bolt_streq (domain->syspath, syspath))
{
bolt_warn (LOG_TOPIC ("domain"), LOG_DOM (domain),
"already connected domain at '%s'"
"reconnected at '%s'", domain->syspath, syspath);
g_free (domain->syspath);
g_free (domain->id);
}
security = bolt_sysfs_security_for_device (dev, &err);
if (security == BOLT_SECURITY_UNKNOWN)
{
bolt_warn_err (err, LOG_TOPIC ("udev"),
"error getting security from sysfs");
g_clear_error (&err);
}
ok = bolt_sysfs_read_iommu (dev, &iommu, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("udev"),
"failed to read iommu");
g_clear_error (&err);
}
g_object_freeze_notify (G_OBJECT (domain));
domain->id = g_strdup (id);
domain->syspath = g_strdup (syspath);
domain->security = security;
domain->iommu = iommu;
g_object_notify_by_pspec (G_OBJECT (domain), props[PROP_ID]);
g_object_notify_by_pspec (G_OBJECT (domain), props[PROP_SYSPATH]);
g_object_notify_by_pspec (G_OBJECT (domain), props[PROP_SECURITY]);
g_object_notify_by_pspec (G_OBJECT (domain), props[PROP_IOMMU]);
ok = bolt_sysfs_read_boot_acl (dev, &acl, &err);
if (!ok)
bolt_warn_err (err, "failed to get boot_acl");
bolt_domain_bootacl_sync (domain, &acl);
bolt_domain_bootacl_update (domain, &acl, NULL);
g_object_thaw_notify (G_OBJECT (domain));
bolt_msg (LOG_DOM (domain), "connected: as %s [%s] (%s)",
id, bolt_security_to_string (security), syspath);
}
void
bolt_domain_disconnected (BoltDomain *domain)
{
g_return_if_fail (BOLT_IS_DOMAIN (domain));
bolt_msg (LOG_DOM (domain), "disconnected from %s", domain->syspath);
g_object_freeze_notify (G_OBJECT (domain));
g_clear_pointer (&domain->id, g_free);
g_clear_pointer (&domain->syspath, g_free);
g_object_notify_by_pspec (G_OBJECT (domain), props[PROP_ID]);
g_object_notify_by_pspec (G_OBJECT (domain), props[PROP_SYSPATH]);
g_object_thaw_notify (G_OBJECT (domain));
}
void
bolt_domain_update_from_udev (BoltDomain *domain,
struct udev_device *udev)
{
g_autoptr(GError) err = NULL;
g_auto(GStrv) acl = NULL;
gboolean ok;
g_return_if_fail (BOLT_IS_DOMAIN (domain));
g_return_if_fail (udev != NULL);
ok = bolt_sysfs_read_boot_acl (udev, &acl, &err);
if (!ok)
{
bolt_warn_err (err, "failed to get boot_acl");
return;
}
bolt_domain_bootacl_update (domain, &acl, NULL);
}
gboolean
bolt_domain_can_delete (BoltDomain *domain,
GError **error)
{
BoltJournal *log;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
log = domain->acllog;
if (log && !bolt_journal_is_fresh (log))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY,
"boot acl journal is not empty");
return FALSE;
}
return TRUE;
}
gboolean
bolt_domain_supports_bootacl (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
return !bolt_strv_isempty (domain->bootacl);
}
guint
bolt_domain_bootacl_slots (BoltDomain *domain,
guint *n_free)
{
guint slots = 0;
guint unused = 0;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), 0);
if (bolt_strv_isempty (domain->bootacl))
{
if (n_free)
*n_free = 0;
return 0;
}
for (char **iter = domain->bootacl; *iter; iter++)
{
slots++;
if (bolt_strzero (*iter))
unused++;
}
if (n_free)
*n_free = unused;
return slots;
}
gboolean
bolt_domain_bootacl_contains (BoltDomain *domain,
const char *uuid)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
g_return_val_if_fail (uuid != NULL, FALSE);
return domain->bootacl != NULL &&
g_strv_contains ((char const * const *) domain->bootacl, uuid);
}
/* domain list management */
BoltDomain *
bolt_domain_insert (BoltDomain *list, BoltDomain *domain)
{
BoltList iter;
BoltList *n;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), list);
/* the list as a whole takes one reference */
g_object_ref (domain);
if (list == NULL)
return domain;
bolt_nhlist_iter_init (&iter, &list->domains);
while ((n = bolt_nhlist_iter_next (&iter)))
{
BoltDomain *d = bolt_list_entry (n, BoltDomain, domains);
if (domain->sort > d->sort)
break;
}
/* all existing domains are sorted before,
* so add to the end of the list */
if (n == NULL)
n = list->domains.prev;
bolt_list_add_after (n, &domain->domains);
return list;
}
BoltDomain *
bolt_domain_remove (BoltDomain *list, BoltDomain *domain)
{
BoltList *head;
g_return_val_if_fail (BOLT_IS_DOMAIN (list), NULL);
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), list);
head = bolt_nhlist_del (&list->domains, &domain->domains);
/* the list as a whole has one reference, release it */
g_object_unref (domain);
if (head == NULL)
return NULL;
return bolt_list_entry (head, BoltDomain, domains);
}
const char **
bolt_domain_bootacl_get_used (BoltDomain *domain,
guint *n_used)
{
GPtrArray *res;
guint used = 0;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
res = g_ptr_array_new ();
for (char **iter = domain->bootacl; iter && *iter; iter++)
{
if (strlen (*iter))
{
g_ptr_array_add (res, *iter);
used++;
}
}
g_ptr_array_add (res, NULL);
if (n_used != NULL)
*n_used = used;
return (const char **) g_ptr_array_free (res, FALSE);
}
void
bolt_domain_bootacl_allocate (BoltDomain *domain,
GStrv acl,
const char *uuid)
{
char **target;
gboolean ok;
gint slot = -1;
g_return_if_fail (BOLT_IS_DOMAIN (domain));
g_return_if_fail (acl != NULL && *acl != NULL);
g_return_if_fail (uuid != NULL);
/* find the first empty slot, if there is any */
target = bolt_strv_contains (acl, "");
if (target)
slot = target - acl;
bolt_debug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"slot before allocation: %d", slot);
g_signal_emit (domain, signals[SIGNAL_BOOTACL_ALLOC], 0,
acl, uuid, &slot, &ok);
bolt_debug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"slot after allocation: %d [handled: %s]",
slot, bolt_yesno (ok));
if (slot == -1)
{
/* no slot was allocated so far, lets do FIFO */
target = bolt_strv_rotate_left (acl);
slot = target - acl;
}
bolt_debug (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"adding '%s' as bootacl[%d] (was '%s')",
uuid, slot, acl[slot]);
bolt_set_strdup (&acl[slot], uuid);
}
gboolean
bolt_domain_bootacl_set (BoltDomain *domain,
GStrv acl,
GError **error)
{
g_autoptr(GHashTable) diff = NULL;
g_auto(GStrv) tmp = NULL;
BoltJournal *log;
gboolean online;
gboolean same;
gboolean ok;
guint ours, theirs;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ok = bolt_domain_bootacl_can_update (domain, error);
if (!ok)
return FALSE;
online = domain->syspath != NULL;
log = domain->acllog;
theirs = bolt_gstrv_length0 (acl);
ours = g_strv_length (domain->bootacl);
if (ours != theirs)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"boot ACL length mismatch (ours: %u yours: %u)",
ours, theirs);
return FALSE;
}
same = bolt_strv_equal (acl, domain->bootacl);
/* NB: we return FALSE but set no error */
if (same == TRUE)
return FALSE;
diff = bolt_strv_diff (domain->bootacl, acl);
if (online)
ok = bolt_sysfs_write_boot_acl (domain->syspath, acl, error);
else
ok = bolt_journal_put_diff (log, diff, error);
if (!ok)
return FALSE;
tmp = g_strdupv (acl);
bolt_domain_bootacl_update (domain, &tmp, diff);
return TRUE;
}
gboolean
bolt_domain_bootacl_add (BoltDomain *domain,
const char *uuid,
GError **error)
{
g_auto(GStrv) acl = NULL;
BoltJournal *log = NULL;
gboolean online;
gboolean ok;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
g_return_val_if_fail (uuid != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ok = bolt_domain_bootacl_can_update (domain, error);
if (!ok)
return FALSE;
if (bolt_domain_bootacl_contains (domain, uuid))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
"'%s' already in boot ACL of domain '%s'",
uuid, domain->id);
return FALSE;
}
online = domain->syspath != NULL;
log = domain->acllog;
acl = g_strdupv (domain->bootacl);
bolt_domain_bootacl_allocate (domain, acl, uuid);
if (online)
ok = bolt_sysfs_write_boot_acl (domain->syspath, acl, error);
else
ok = bolt_journal_put (log, uuid, BOLT_JOURNAL_ADDED, error);
if (!ok)
return FALSE;
bolt_domain_bootacl_update (domain, &acl, NULL);
return TRUE;
}
gboolean
bolt_domain_bootacl_del (BoltDomain *domain,
const char *uuid,
GError **error)
{
g_autoptr(GHashTable) diff = NULL;
g_auto(GStrv) acl = NULL;
BoltJournal *log;
gboolean ok;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
g_return_val_if_fail (uuid != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ok = bolt_domain_bootacl_can_update (domain, error);
if (!ok)
return FALSE;
log = domain->acllog;
acl = g_strdupv (domain->bootacl);
ok = bolt_domain_bootacl_remove (domain, acl, uuid, error);
if (!ok)
return FALSE;
if (domain->syspath != NULL)
ok = bolt_sysfs_write_boot_acl (domain->syspath, acl, error);
else
ok = bolt_journal_put (log, uuid, BOLT_JOURNAL_REMOVED, error);
if (!ok)
return FALSE;
diff = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (diff, (gpointer) uuid, GINT_TO_POINTER ('-'));
bolt_domain_bootacl_update (domain, &acl, diff);
return TRUE;
}
BoltDomain *
bolt_domain_next (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
return bolt_list_entry (domain->domains.next, BoltDomain, domains);
}
BoltDomain *
bolt_domain_prev (BoltDomain *domain)
{
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
return bolt_list_entry (domain->domains.prev, BoltDomain, domains);
}
guint
bolt_domain_count (BoltDomain *domain)
{
if (domain == NULL)
return 0;
return bolt_nhlist_len (&domain->domains);
}
void
bolt_domain_foreach (BoltDomain *list,
GFunc func,
gpointer data)
{
BoltList iter;
BoltList *n;
if (list == NULL)
return;
bolt_nhlist_iter_init (&iter, &list->domains);
while ((n = bolt_nhlist_iter_next (&iter)))
{
BoltDomain *d = bolt_list_entry (n, BoltDomain, domains);
func ((gpointer) d, data);
}
}
BoltDomain *
bolt_domain_find_id (BoltDomain *list,
const char *id,
GError **error)
{
BoltList iter;
BoltList *n;
g_return_val_if_fail (id != NULL, NULL);
if (list == NULL)
goto notfound;
bolt_nhlist_iter_init (&iter, &list->domains);
while ((n = bolt_nhlist_iter_next (&iter)))
{
BoltDomain *d = bolt_list_entry (n, BoltDomain, domains);
if (bolt_streq (d->id, id) || bolt_streq (d->uid, id))
return d;
}
notfound:
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"domain with id '%s' could not be found.",
id);
return NULL;
}
void
bolt_domain_clear (BoltDomain **list)
{
BoltDomain *iter;
g_return_if_fail (list != NULL);
iter = *list;
while (bolt_domain_count (iter))
iter = bolt_domain_remove (iter, iter);
*list = iter;
}
/* dbus property setter */
static gboolean
handle_set_bootacl (BoltExported *obj,
const char *name,
const GValue *value,
GError **error)
{
BoltDomain *domain = BOLT_DOMAIN (obj);
GStrv acl;
gboolean ok;
acl = (GStrv) g_value_get_boxed (value);
if (!bolt_uuidv_check (acl, TRUE, error))
return FALSE;
/* does check if we can actually update the boot-acl,
* i.e. calls bolt_domain_bootacl_can_update */
ok = bolt_domain_bootacl_set (domain, acl, error);
// maybe adjust the G_IO_ERROR to a G_DBUS_ERROR ?
return ok;
}
bolt-0.9.2/boltd/bolt-domain.h 0000664 0000000 0000000 00000011066 14174530510 0016150 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-exported.h"
#include
G_BEGIN_DECLS
/* forward declaration */
struct udev_device;
#define BOLT_TYPE_DOMAIN bolt_domain_get_type ()
G_DECLARE_FINAL_TYPE (BoltDomain, bolt_domain, BOLT, DOMAIN, BoltExported);
BoltDomain * bolt_domain_new_for_udev (struct udev_device *udev,
const char *uid,
GError **error) G_GNUC_WARN_UNUSED_RESULT;
const char * bolt_domain_get_uid (BoltDomain *domain);
const char * bolt_domain_get_id (BoltDomain *domain);
const char * bolt_domain_get_syspath (BoltDomain *domain);
BoltSecurity bolt_domain_get_security (BoltDomain *domain);
GStrv bolt_domain_get_bootacl (BoltDomain *domain);
GStrv bolt_domain_dup_bootacl (BoltDomain *domain);
gboolean bolt_domain_is_stored (BoltDomain *domain);
gboolean bolt_domain_is_connected (BoltDomain *domain);
gboolean bolt_domain_has_iommu (BoltDomain *domain);
void bolt_domain_export (BoltDomain *domain,
GDBusConnection *connection);
void bolt_domain_connected (BoltDomain *domain,
struct udev_device *udev);
void bolt_domain_disconnected (BoltDomain *domain);
void bolt_domain_update_from_udev (BoltDomain *domain,
struct udev_device *udev);
gboolean bolt_domain_can_delete (BoltDomain *domain,
GError **error);
/* boot acl related functions */
gboolean bolt_domain_supports_bootacl (BoltDomain *domain);
guint bolt_domain_bootacl_slots (BoltDomain *domain,
guint *n_free);
gboolean bolt_domain_bootacl_contains (BoltDomain *domain,
const char *uuid);
const char ** bolt_domain_bootacl_get_used (BoltDomain *domain,
guint *n_used);
void bolt_domain_bootacl_allocate (BoltDomain *domain,
GStrv acl,
const char *uuid);
gboolean bolt_domain_bootacl_set (BoltDomain *domain,
GStrv acl,
GError **error);
gboolean bolt_domain_bootacl_add (BoltDomain *domain,
const char *uuid,
GError **error);
gboolean bolt_domain_bootacl_del (BoltDomain *domain,
const char *uuid,
GError **error);
/* domain list management */
BoltDomain * bolt_domain_insert (BoltDomain *list,
BoltDomain *domain) G_GNUC_WARN_UNUSED_RESULT;
BoltDomain * bolt_domain_remove (BoltDomain *list,
BoltDomain *domain) G_GNUC_WARN_UNUSED_RESULT;
BoltDomain * bolt_domain_next (BoltDomain *domain);
BoltDomain * bolt_domain_prev (BoltDomain *domain);
guint bolt_domain_count (BoltDomain *domain);
void bolt_domain_foreach (BoltDomain *list,
GFunc func,
gpointer user_data);
BoltDomain * bolt_domain_find_id (BoltDomain *list,
const char *id,
GError **error);
void bolt_domain_clear (BoltDomain **list);
G_END_DECLS
bolt-0.9.2/boltd/bolt-exported.c 0000664 0000000 0000000 00000103431 14174530510 0016524 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-dbus.h"
#include "bolt-enums.h"
#include "bolt-error.h"
#include "bolt-glue.h"
#include "bolt-log.h"
#include "bolt-names.h"
#include "bolt-str.h"
#include "bolt-exported.h"
typedef struct _BoltExportedMethod BoltExportedMethod;
typedef struct _BoltExportedProp BoltExportedProp;
static GVariant * bolt_exported_get_prop (BoltExported *exported,
BoltExportedProp *prop);
static char * bolt_exported_make_object_path (BoltExported *exported);
static void bolt_exported_dispatch_properties_changed (GObject *object,
guint n_pspecs,
GParamSpec **pspecs);
static gboolean handle_authorize_method_default (BoltExported *exported,
GDBusMethodInvocation *inv,
GError **error);
static gboolean handle_authorize_property_default (BoltExported *exported,
const char *name,
gboolean setting,
GDBusMethodInvocation *invocation,
GError **error);
static void bolt_exported_method_free (gpointer data);
static void bolt_exported_prop_free (gpointer data);
struct _BoltExportedMethod
{
char *name;
BoltExportedMethodHandler handler;
};
struct _BoltExportedProp
{
GParamSpec *spec;
const char *name_obj; /* shortcut for spec->name */
const char *name_bus;
GVariantType *signature;
/* optional */
BoltExportedSetter setter;
/* auto string conversion */
BoltWireConv *conv;
};
struct _BoltExportedClassPrivate
{
char *iface_name;
GDBusInterfaceInfo *iface_info;
char *object_path;
GHashTable *methods;
GHashTable *properties;
};
typedef struct _BoltExportedPrivate
{
GDBusConnection *dbus;
char *object_path;
/* if exported */
guint registration;
/* property changes */
GPtrArray *props_changed;
guint props_changed_id;
} BoltExportedPrivate;
static gpointer bolt_exported_parent_class = NULL;
static gint BoltExported_private_offset = 0;
static void bolt_exported_init (GTypeInstance *,
gpointer g_class);
static void bolt_exported_class_init (BoltExportedClass *klass);
static void bolt_exported_base_init (gpointer g_class);
static void bolt_exported_base_finalize (gpointer g_class);
#define GET_PRIV(self) G_STRUCT_MEMBER_P (self, BoltExported_private_offset)
#define CHAIN_UP(method) G_OBJECT_CLASS (bolt_exported_parent_class)->method
GType
bolt_exported_get_type (void)
{
static volatile gsize exported_type = 0;
if (g_once_init_enter (&exported_type))
{
GType type_id;
const GTypeInfo type_info = {
sizeof (BoltExportedClass),
bolt_exported_base_init,
(GBaseFinalizeFunc) bolt_exported_base_finalize,
(GClassInitFunc) bolt_exported_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (BoltExported),
0, /* n_preallocs */
bolt_exported_init,
NULL, /* value_table */
};
type_id = g_type_register_static (G_TYPE_OBJECT, "BoltExported",
&type_info, G_TYPE_FLAG_ABSTRACT);
g_type_add_class_private (type_id, sizeof (BoltExportedClassPrivate));
BoltExported_private_offset =
g_type_add_instance_private (type_id, sizeof (BoltExportedPrivate));
g_once_init_leave (&exported_type, type_id);
}
return exported_type;
}
enum {
PROP_0,
PROP_OBJECT_ID,
PROP_OBJECT_PATH,
PROP_EXPORTED,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = {NULL, };
enum {
SIGNAL_AUTHORIZE_METHOD,
SIGNAL_AUTHORIZE_PROPERTY,
SIGNAL_LAST
};
static guint signals[SIGNAL_LAST] = {0, };
static void
bolt_exported_finalize (GObject *object)
{
BoltExported *exported = BOLT_EXPORTED (object);
BoltExportedPrivate *priv = GET_PRIV (exported);
if (bolt_exported_is_exported (exported))
bolt_exported_unexport (exported);
g_clear_pointer (&priv->object_path, g_free);
g_ptr_array_free (priv->props_changed, TRUE);
G_OBJECT_CLASS (bolt_exported_parent_class)->finalize (object);
}
static void
bolt_exported_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltExported *exported = BOLT_EXPORTED (object);
BoltExportedPrivate *priv = GET_PRIV (exported);
switch (prop_id)
{
case PROP_OBJECT_ID:
bolt_bug ("BoltExported::object-id must be overridden");
break;
case PROP_OBJECT_PATH:
g_value_set_string (value, priv->object_path);
break;
case PROP_EXPORTED:
g_value_set_boolean (value, priv->registration > 0);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_exported_init (GTypeInstance *instance, gpointer g_class)
{
BoltExported *exported = BOLT_EXPORTED (instance);
BoltExportedPrivate *priv = GET_PRIV (exported);
priv->props_changed = g_ptr_array_new ();
}
static void
bolt_exported_class_init (BoltExportedClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
bolt_exported_parent_class = g_type_class_peek_parent (klass);
g_type_class_adjust_private_offset (klass, &BoltExported_private_offset);
gobject_class->finalize = bolt_exported_finalize;
gobject_class->get_property = bolt_exported_get_property;
gobject_class->dispatch_properties_changed = bolt_exported_dispatch_properties_changed;
klass->authorize_method = handle_authorize_method_default;
klass->authorize_property = handle_authorize_property_default;
props[PROP_OBJECT_ID] =
g_param_spec_string ("object-id",
NULL, NULL,
NULL,
G_PARAM_READABLE |
G_PARAM_STATIC_NICK);
props[PROP_OBJECT_PATH] =
g_param_spec_string ("object-path",
NULL, NULL,
NULL,
G_PARAM_READABLE |
G_PARAM_STATIC_NICK);
props[PROP_EXPORTED] =
g_param_spec_boolean ("exported", NULL, NULL,
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_NICK);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
signals[SIGNAL_AUTHORIZE_METHOD] =
g_signal_new ("authorize-method",
BOLT_TYPE_EXPORTED,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (BoltExportedClass, authorize_method),
g_signal_accumulator_first_wins,
NULL,
NULL,
G_TYPE_BOOLEAN,
2,
G_TYPE_DBUS_METHOD_INVOCATION,
G_TYPE_POINTER);
signals[SIGNAL_AUTHORIZE_PROPERTY] =
g_signal_new ("authorize-property",
BOLT_TYPE_EXPORTED,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (BoltExportedClass, authorize_property),
g_signal_accumulator_first_wins,
NULL,
NULL,
G_TYPE_BOOLEAN,
4,
G_TYPE_STRING,
G_TYPE_BOOLEAN,
G_TYPE_DBUS_METHOD_INVOCATION,
G_TYPE_POINTER);
}
static void
bolt_exported_base_init (gpointer g_class)
{
BoltExportedClass *klass = g_class;
klass->priv = G_TYPE_CLASS_GET_PRIVATE (g_class, BOLT_TYPE_EXPORTED, BoltExportedClassPrivate);
memset (klass->priv, 0, sizeof (BoltExportedClassPrivate));
klass->priv->methods = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, bolt_exported_method_free);
klass->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, bolt_exported_prop_free);
}
static void
bolt_exported_base_finalize (gpointer g_class)
{
BoltExportedClass *klass = g_class;
BoltExportedClassPrivate *priv = klass->priv;
if (priv->iface_info)
{
g_dbus_interface_info_unref (priv->iface_info);
priv->iface_info = NULL;
}
g_hash_table_unref (priv->properties);
g_hash_table_unref (priv->methods);
g_clear_pointer (&priv->iface_name, g_free);
g_clear_pointer (&priv->object_path, g_free);
}
/* internal utility functions */
static const char *
bolt_exported_get_iface_name (BoltExported *exported)
{
BoltExportedClass *klass;
klass = BOLT_EXPORTED_GET_CLASS (exported);
return klass->priv->iface_name;
}
static BoltExportedProp *
bolt_exported_lookup_property (BoltExported *exported,
const char *name,
GError **error)
{
BoltExportedClass *klass;
BoltExportedProp *prop;
if (name == NULL)
return NULL;
klass = BOLT_EXPORTED_GET_CLASS (exported);
prop = g_hash_table_lookup (klass->priv->properties, name);
if (prop == NULL)
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY,
"no such property: %s", name);
return prop;
}
static BoltExportedMethod *
bolt_exported_lookup_method (BoltExported *exported,
const char *name,
GError **error)
{
BoltExportedClass *klass;
BoltExportedMethod *method;
if (name == NULL)
return NULL;
klass = BOLT_EXPORTED_GET_CLASS (exported);
method = g_hash_table_lookup (klass->priv->methods, name);
if (method == NULL)
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
"no such method: %s", name);
return method;
}
static GVariant *
bolt_exported_get_prop (BoltExported *exported,
BoltExportedProp *prop)
{
g_autoptr(GError) err = NULL;
g_auto(GValue) res = G_VALUE_INIT;
const char *name;
const GParamSpec *spec;
GVariant *ret;
name = prop->name_obj;
spec = prop->spec;
g_value_init (&res, spec->value_type);
g_object_get_property (G_OBJECT (exported), name, &res);
ret = bolt_wire_conv_to_wire (prop->conv, &res, &err);
if (ret == NULL)
bolt_bug ("failed to serialize value for prop %s: %s",
prop->spec->name, err->message);
return ret;
}
static char *
bolt_exported_make_object_path (BoltExported *exported)
{
g_autofree char *id = NULL;
BoltExportedClass *klass;
const char *base;
klass = BOLT_EXPORTED_GET_CLASS (exported);
base = klass->priv->object_path;
g_object_get (exported, "object-id", &id, NULL);
return bolt_gen_object_path (base, id);
}
/* dispatch helper function */
typedef struct _DispatchData
{
GDBusMethodInvocation *inv;
gboolean is_property;
union
{
BoltExportedMethod *method;
BoltExportedProp *prop;
};
} DispatchData;
static void
dispatch_data_free (DispatchData *data)
{
g_slice_free (DispatchData, data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (DispatchData, dispatch_data_free);
static GVariant *
dispach_property_setter (BoltExported *exported,
GDBusMethodInvocation *inv,
BoltExportedProp *prop,
GError **error)
{
g_autoptr(GError) err = NULL;
g_auto(GValue) val = G_VALUE_INIT;
g_autoptr(GVariant) vin = NULL;
GVariant *params;
gboolean ok;
params = g_dbus_method_invocation_get_parameters (inv);
g_variant_get_child (params, 2, "v", &vin);
g_value_init (&val, prop->spec->value_type);
ok = bolt_wire_conv_from_wire (prop->conv, vin, &val, &err);
if (ok)
ok = prop->setter (exported, prop->name_obj, &val, &err);
if (!ok && err != NULL)
{
bolt_error_propagate (error, &err);
return NULL;
}
else if (!ok)
{
bolt_critical (LOG_TOPIC ("dbus"),
"property setter signaled error, but no error is set");
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"%s", "could not set property");
return NULL;
}
g_object_notify_by_pspec (G_OBJECT (exported), prop->spec);
return g_variant_new ("()");
}
static GVariant *
dispatch_method_call (BoltExported *exported,
GDBusMethodInvocation *inv,
BoltExportedMethod *method,
GError **error)
{
GVariant *params = g_dbus_method_invocation_get_parameters (inv);
GVariant *res;
res = method->handler (exported, params, inv, error);
return res;
}
static void
query_authorization_done (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) err = NULL;
g_autoptr(DispatchData) data = user_data;
GDBusMethodInvocation *inv = data->inv;
BoltExported *exported = BOLT_EXPORTED (source_object);
GVariant *ret;
gboolean ok;
ok = g_task_propagate_boolean (G_TASK (res), &err);
bolt_debug (LOG_TOPIC ("dbus"), "authorization done: %s", bolt_yesno (ok));
if (!ok && err == NULL)
{
bolt_bug ("negative auth result, but no GError set");
g_set_error_literal (&err, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
"access denied");
}
if (!ok)
{
g_dbus_method_invocation_return_gerror (inv, err);
return;
}
if (data->is_property)
ret = dispach_property_setter (exported, inv, data->prop, &err);
else
ret = dispatch_method_call (exported, inv, data->method, &err);
if (ret == NULL && err != NULL)
g_dbus_method_invocation_return_gerror (inv, err);
else if (ret != NULL)
g_dbus_method_invocation_return_value (inv, ret);
/* else: must have been handled by the method call directly */
}
static void
query_authorization (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
GError *error = NULL;
BoltExported *exported = source_object;
DispatchData *data = task_data;
gboolean authorized = FALSE;
if (data->is_property)
{
const char *method_name = g_dbus_method_invocation_get_method_name (data->inv);
gboolean is_setter = bolt_streq (method_name, "Set");
g_signal_emit (exported,
signals[SIGNAL_AUTHORIZE_PROPERTY],
0,
data->prop->name_obj,
is_setter,
data->inv,
&error,
&authorized);
}
else
{
g_signal_emit (exported,
signals[SIGNAL_AUTHORIZE_METHOD],
0,
data->inv,
&error,
&authorized);
}
bolt_debug (LOG_TOPIC ("dbus"), "query_authorization returned: %s",
bolt_yesno (authorized));
if (!authorized)
g_task_return_error (task, error);
else
g_task_return_boolean (task, authorized);
}
static gboolean
handle_authorize_method_default (BoltExported *exported,
GDBusMethodInvocation *inv,
GError **error)
{
const char *method_name;
method_name = g_dbus_method_invocation_get_method_name (inv);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
"bolt operation '%s' denied by default policy",
method_name);
return FALSE;
}
static gboolean
handle_authorize_property_default (BoltExported *exported,
const char *name,
gboolean setting,
GDBusMethodInvocation *inv,
GError **error)
{
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
"setting property '%s' denied by default policy",
name);
return FALSE;
}
/* DBus virtual table */
static void
handle_dbus_method_call (GDBusConnection *connection,
const char *sender,
const char *object_path,
const char *interface_name,
const char *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(GError) err = NULL;
BoltExported *exported;
gboolean is_property;
DispatchData *data;
exported = BOLT_EXPORTED (user_data);
bolt_debug (LOG_TOPIC ("dbus"), "method call: %s.%s at %s from %s",
interface_name, method_name, object_path, sender);
/* we also handle property setting here */
is_property = bolt_streq (interface_name, "org.freedesktop.DBus.Properties");
data = g_slice_new0 (DispatchData);
data->inv = invocation;
data->is_property = is_property;
if (is_property)
{
const GDBusPropertyInfo *pi;
BoltExportedProp *prop = NULL;
gboolean is_setter;
pi = g_dbus_method_invocation_get_property_info (invocation);
if (pi != NULL)
prop = bolt_exported_lookup_property (exported, pi->name, &err);
else
g_set_error (&err, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"property information missing");
is_setter = bolt_streq (method_name, "Set");
if (is_setter && prop != NULL && prop->setter == NULL)
g_set_error (&err, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"property: %s has no setter", prop->name_bus);
data->prop = prop;
}
else
{
BoltExportedMethod *method;
method = bolt_exported_lookup_method (exported, method_name, &err);
data->method = method;
}
if (err != NULL)
{
//bolt_warn_err (err, LOG_TOPIC ("dbus"), "error dispatching call");
g_dbus_method_invocation_return_gerror (invocation, err);
return;
}
task = g_task_new (exported, NULL, query_authorization_done, data);
g_task_set_source_tag (task, handle_dbus_method_call);
g_task_set_task_data (task, data, NULL);
g_task_run_in_thread (task, query_authorization);
}
static GVariant *
handle_dbus_get_property (GDBusConnection *connection,
const char *sender,
const char *object_path,
const char *interface_name,
const char *property_name,
GError **error,
gpointer user_data)
{
g_autoptr(GError) err = NULL;
BoltExported *exported;
BoltExportedProp *prop;
GVariant *ret;
exported = BOLT_EXPORTED (user_data);
bolt_debug (LOG_TOPIC ("dbus"), "get property: %s.%s at %s from %s",
interface_name, property_name, object_path, sender);
prop = bolt_exported_lookup_property (exported, property_name, &err);
if (prop == NULL)
{
bolt_warn_err (err, LOG_TOPIC ("dbus"), "get_property");
bolt_error_propagate (error, &err);
return NULL;
}
ret = bolt_exported_get_prop (exported, prop);
return ret;
}
static void
bolt_exported_dispatch_properties_changed (GObject *object,
guint n_pspecs,
GParamSpec **pspecs)
{
g_autoptr(GVariant) changes = NULL;
g_autoptr(GError) err = NULL;
g_auto(GVariantBuilder) changed;
g_auto(GVariantBuilder) invalidated;
const char *iface_name;
BoltExported *exported;
BoltExportedPrivate *priv;
gboolean ok;
guint count = 0;
exported = BOLT_EXPORTED (object);
priv = GET_PRIV (exported);
g_variant_builder_init (&changed, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_init (&invalidated, G_VARIANT_TYPE ("as"));
/* no bus, no changed signal */
if (priv->dbus == NULL || priv->object_path == NULL)
goto out;
for (guint i = 0; i < n_pspecs; i++)
{
g_autoptr(GVariant) var = NULL;
GParamSpec *pspec = pspecs[i];
BoltExportedProp *prop;
const char *nick;
nick = g_param_spec_get_nick (pspec);
prop = bolt_exported_lookup_property (exported, nick, NULL);
if (prop == NULL)
{
bolt_debug (LOG_TOPIC ("dbus"), "prop %s change ignored", nick);
continue;
}
bolt_debug (LOG_TOPIC ("dbus"), "prop %s changed", nick);
var = bolt_exported_get_prop (exported, prop);
g_variant_builder_add (&changed, "{sv}", prop->name_bus, var);
count++;
}
if (count == 0)
goto out;
iface_name = bolt_exported_get_iface_name (exported);
changes = g_variant_ref_sink (g_variant_new ("(sa{sv}as)",
iface_name,
&changed,
&invalidated));
ok = g_dbus_connection_emit_signal (priv->dbus,
NULL,
priv->object_path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
changes,
&err);
if (!ok)
bolt_warn_err (err, LOG_TOPIC ("dbus"),
"error emitting property changes");
bolt_debug (LOG_TOPIC ("dbus"), "emitted property %u changes", count);
out:
CHAIN_UP (dispatch_properties_changed) (object, n_pspecs, pspecs);
}
static GDBusInterfaceVTable dbus_vtable = {
handle_dbus_method_call,
handle_dbus_get_property,
NULL, /* set_property (handled by method call) */
};
/* public methods: class */
void
bolt_exported_class_set_interface_name (BoltExportedClass *klass,
const char *name)
{
g_return_if_fail (BOLT_IS_EXPORTED_CLASS (klass));
g_return_if_fail (klass->priv != NULL);
g_return_if_fail (klass->priv->iface_name == NULL);
klass->priv->iface_name = g_strdup (name);
}
void
bolt_exported_class_set_interface_info (BoltExportedClass *klass,
const char *iface_name,
const char *resource_name)
{
g_autoptr(GError) err = NULL;
GDBusInterfaceInfo *info = NULL;
g_return_if_fail (BOLT_IS_EXPORTED_CLASS (klass));
g_return_if_fail (klass->priv != NULL);
g_return_if_fail (klass->priv->iface_info == NULL);
g_return_if_fail (iface_name != NULL);
g_return_if_fail (resource_name != NULL);
bolt_exported_class_set_interface_name (klass, iface_name);
info = bolt_dbus_interface_info_lookup (resource_name,
iface_name,
&err);
if (info == NULL)
bolt_error (LOG_TOPIC ("dbus"), LOG_ERR (err),
"could not set interface info");
klass->priv->iface_info = info; /* transfer ownership */
}
void
bolt_exported_class_set_object_path (BoltExportedClass *klass,
const char *base_path)
{
g_return_if_fail (BOLT_IS_EXPORTED_CLASS (klass));
g_return_if_fail (klass->priv != NULL);
g_return_if_fail (klass->priv->object_path == NULL);
g_return_if_fail (base_path != NULL);
g_return_if_fail (g_variant_is_object_path (base_path));
klass->priv->object_path = g_strdup (base_path);
}
void
bolt_exported_class_export_property (BoltExportedClass *klass,
GParamSpec *spec)
{
BoltExportedClassPrivate *priv;
GDBusInterfaceInfo *iface = NULL;
GDBusPropertyInfo *info = NULL;
GDBusPropertyInfo **iter;
BoltExportedProp *prop;
const char *name_bus, *name_obj;
if (!klass || !BOLT_IS_EXPORTED_CLASS (klass))
{
bolt_error (LOG_TOPIC ("dbus"), "klass not a BoltExportedClass");
return;
}
g_return_if_fail (G_IS_PARAM_SPEC (spec));
priv = klass->priv;
name_obj = g_param_spec_get_name (spec);
name_bus = g_param_spec_get_nick (spec);
iface = priv->iface_info;
for (iter = iface->properties; iter && *iter; iter++)
{
GDBusPropertyInfo *pi = *iter;
if (bolt_streq (pi->name, name_bus))
{
info = pi;
break;
}
}
if (info == NULL)
{
bolt_error (LOG_TOPIC ("dbus"), "no property info for %s", name_bus);
return;
}
prop = g_new0 (BoltExportedProp, 1);
prop->spec = g_param_spec_ref (spec);
prop->name_bus = name_bus;
prop->name_obj = name_obj;
prop->signature = g_variant_type_new (info->signature);
prop->conv = bolt_wire_conv_for (prop->signature, prop->spec);
bolt_debug (LOG_TOPIC ("dbus"), "installed prop: %s -> %s [%s]",
prop->name_bus, prop->name_obj,
bolt_wire_conv_describe (prop->conv));
g_hash_table_insert (priv->properties, (gpointer) prop->name_bus, prop);
}
void
bolt_exported_class_export_properties (BoltExportedClass *klass,
guint start,
guint n_pspecs,
GParamSpec **specs)
{
g_return_if_fail (BOLT_IS_EXPORTED_CLASS (klass));
g_return_if_fail (start > 0);
for (guint i = start; i < n_pspecs; i++)
bolt_exported_class_export_property (klass, specs[i]);
}
void
bolt_exported_class_property_setter (BoltExportedClass *klass,
GParamSpec *spec,
BoltExportedSetter setter)
{
BoltExportedProp *prop;
const char *nick;
if (!klass || !BOLT_IS_EXPORTED_CLASS (klass))
{
bolt_error (LOG_TOPIC ("dbus"), "klass not a BoltExportedClass");
return;
}
nick = g_param_spec_get_nick (spec);
prop = g_hash_table_lookup (klass->priv->properties, nick);
if (prop == NULL)
{
bolt_error (LOG_TOPIC ("dbus"), "unknown property: %s", nick);
return;
}
prop->setter = setter;
}
void
bolt_exported_class_property_wireconv (BoltExportedClass *klass,
GParamSpec *spec,
const char *custom_id,
BoltConvToWire to_wire,
BoltConvFromWire from_wire)
{
BoltExportedProp *prop;
BoltWireConv *conv;
const char *nick;
g_return_if_fail (BOLT_IS_EXPORTED_CLASS (klass));
g_return_if_fail (G_IS_PARAM_SPEC (spec));
nick = g_param_spec_get_nick (spec);
prop = g_hash_table_lookup (klass->priv->properties, nick);
if (prop == NULL)
{
bolt_bug (LOG_TOPIC ("dbus"), "unknown property: %s", nick);
return;
}
conv = bolt_wire_conv_custom (prop->signature,
prop->spec,
custom_id,
to_wire,
from_wire);
bolt_wire_conv_unref (prop->conv);
prop->conv = conv; /* transfer the reference */
bolt_debug (LOG_TOPIC ("dbus"), "+adjusted prop: wireconv: %s [%s]",
prop->name_bus,
bolt_wire_conv_describe (prop->conv));
}
void
bolt_exported_class_export_method (BoltExportedClass *klass,
const char *name,
BoltExportedMethodHandler handler)
{
BoltExportedMethod *method;
g_return_if_fail (BOLT_IS_EXPORTED_CLASS (klass));
g_return_if_fail (name != NULL);
g_return_if_fail (handler != NULL);
method = g_new0 (BoltExportedMethod, 1);
method->name = g_strdup (name);
method->handler = handler;
g_hash_table_insert (klass->priv->methods, method->name, method);
}
/* public methods: instance */
gboolean
bolt_exported_export (BoltExported *exported,
GDBusConnection *connection,
const char *path_hint,
GError **error)
{
g_autofree char *object_path = NULL;
BoltExportedPrivate *priv;
BoltExportedClass *klass;
guint id;
g_return_val_if_fail (BOLT_IS_EXPORTED (exported), FALSE);
g_return_val_if_fail (connection != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
priv = GET_PRIV (exported);
klass = BOLT_EXPORTED_GET_CLASS (exported);
if (klass->priv->iface_info == NULL)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"interface information is missing");
return FALSE;
}
object_path = g_strdup (path_hint);
if (object_path == NULL)
{
object_path = bolt_exported_make_object_path (exported);
bolt_debug (LOG_TOPIC ("dbus"), "generated object path: %s",
object_path);
}
if (object_path == NULL)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"object path empty for object");
return FALSE;
}
id = g_dbus_connection_register_object (connection,
object_path,
klass->priv->iface_info,
&dbus_vtable,
exported,
NULL,
error);
if (id == 0)
return FALSE;
bolt_debug (LOG_TOPIC ("dbus"), "registered object at %s", object_path);
priv->dbus = g_object_ref (connection);
priv->object_path = g_steal_pointer (&object_path);
priv->registration = id;
g_object_notify_by_pspec (G_OBJECT (exported), props[PROP_OBJECT_PATH]);
g_object_notify_by_pspec (G_OBJECT (exported), props[PROP_EXPORTED]);
return TRUE;
}
gboolean
bolt_exported_unexport (BoltExported *exported)
{
g_autofree char *opath = NULL;
BoltExportedPrivate *priv;
gboolean ok;
g_return_val_if_fail (BOLT_IS_EXPORTED (exported), FALSE);
priv = GET_PRIV (exported);
if (priv->dbus == NULL || priv->registration == 0)
return FALSE;
ok = g_dbus_connection_unregister_object (priv->dbus, priv->registration);
if (ok)
{
g_clear_object (&priv->dbus);
priv->registration = 0;
opath = g_steal_pointer (&priv->object_path);
g_object_notify_by_pspec (G_OBJECT (exported), props[PROP_OBJECT_PATH]);
g_object_notify_by_pspec (G_OBJECT (exported), props[PROP_EXPORTED]);
}
bolt_debug (LOG_TOPIC ("dbus"), "unregistered object at %s: %s",
opath, bolt_yesno (ok));
return ok;
}
gboolean
bolt_exported_is_exported (BoltExported *exported)
{
BoltExportedPrivate *priv;
g_return_val_if_fail (BOLT_IS_EXPORTED (exported), FALSE);
priv = GET_PRIV (exported);
return priv->registration > 0;
}
GDBusConnection *
bolt_exported_get_connection (BoltExported *exported)
{
BoltExportedPrivate *priv;
g_return_val_if_fail (BOLT_IS_EXPORTED (exported), NULL);
priv = GET_PRIV (exported);
return priv->dbus;
}
const char *
bolt_exported_get_object_path (BoltExported *exported)
{
BoltExportedPrivate *priv;
g_return_val_if_fail (BOLT_IS_EXPORTED (exported), NULL);
priv = GET_PRIV (exported);
return priv->object_path;
}
gboolean
bolt_exported_emit_signal (BoltExported *exported,
const char *name,
GVariant *parameters,
GError **error)
{
g_autoptr(GError) err = NULL;
BoltExportedPrivate *priv;
const char *iface_name;
gboolean ok;
g_return_val_if_fail (BOLT_IS_EXPORTED (exported), FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (parameters != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
priv = GET_PRIV (exported);
/* if we are not exported, we just ignore this */
if (priv->dbus == NULL || priv->object_path == NULL)
return TRUE;
iface_name = bolt_exported_get_iface_name (exported);
ok = g_dbus_connection_emit_signal (priv->dbus,
NULL,
priv->object_path,
iface_name,
name,
parameters,
&err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("dbus"),
"error emitting signal");
bolt_error_propagate (error, &err);
}
else
{
bolt_debug (LOG_TOPIC ("dbus"), "emitted signal: %s", name);
}
return ok;
}
/* non BoltExported internal methods */
static void
bolt_exported_method_free (gpointer data)
{
BoltExportedMethod *method = data;
g_clear_pointer (&method->name, g_free);
g_free (method);
}
static void
bolt_exported_prop_free (gpointer data)
{
BoltExportedProp *prop = data;
g_param_spec_unref (prop->spec);
g_variant_type_free (prop->signature);
bolt_wire_conv_unref (prop->conv);
g_free (prop);
}
bolt-0.9.2/boltd/bolt-exported.h 0000664 0000000 0000000 00000012024 14174530510 0016526 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include
G_BEGIN_DECLS
#define BOLT_TYPE_EXPORTED bolt_exported_get_type ()
G_DECLARE_DERIVABLE_TYPE (BoltExported, bolt_exported, BOLT, EXPORTED, GObject);
typedef struct _BoltExportedClassPrivate BoltExportedClassPrivate;
struct _BoltExportedClass
{
GObjectClass parent_class;
/*< private >*/
BoltExportedClassPrivate *priv;
/*< public >*/
/* Signals */
gboolean (*authorize_method) (BoltExported *exported,
GDBusMethodInvocation *invocation,
GError **error);
gboolean (*authorize_property) (BoltExported *exported,
const char *name,
gboolean setting,
GDBusMethodInvocation *invocation,
GError **error);
/* for the future */
gpointer padding[10];
};
typedef GVariant * (* BoltExportedMethodHandler) (BoltExported *obj,
GVariant *params,
GDBusMethodInvocation *inv,
GError **error);
typedef gboolean (* BoltExportedSetter) (BoltExported *obj,
const char *name,
const GValue *value,
GError **error);
/* class methods */
void bolt_exported_class_set_interface_name (BoltExportedClass *klass,
const char *name);
void bolt_exported_class_set_interface_info (BoltExportedClass *klass,
const char *iface_name,
const char *resource_name);
void bolt_exported_class_set_object_path (BoltExportedClass *klass,
const char *base_path);
void bolt_exported_class_export_property (BoltExportedClass *klass,
GParamSpec *spec);
void bolt_exported_class_export_properties (BoltExportedClass *klass,
guint start,
guint n_pspecs,
GParamSpec **specs);
void bolt_exported_class_property_setter (BoltExportedClass *klass,
GParamSpec *spec,
BoltExportedSetter setter);
void bolt_exported_class_property_wireconv (BoltExportedClass *klass,
GParamSpec *spec,
const char *custom_id,
BoltConvToWire to_wire,
BoltConvFromWire from_wire);
void bolt_exported_class_export_method (BoltExportedClass *klass,
const char *name,
BoltExportedMethodHandler handler);
/* instance methods */
gboolean bolt_exported_export (BoltExported *exported,
GDBusConnection *connection,
const char *object_path,
GError **error);
gboolean bolt_exported_unexport (BoltExported *exported);
gboolean bolt_exported_is_exported (BoltExported *exported);
GDBusConnection * bolt_exported_get_connection (BoltExported *exported);
const char * bolt_exported_get_object_path (BoltExported *exported);
gboolean bolt_exported_emit_signal (BoltExported *exported,
const char *name,
GVariant *parameters,
GError **error);
void bolt_exported_flush (BoltExported *exported);
G_END_DECLS
bolt-0.9.2/boltd/bolt-guard.c 0000664 0000000 0000000 00000036015 14174530510 0015777 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2020 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-error.h"
#include "bolt-guard.h"
#include "bolt-io.h"
#include "bolt-log.h"
#include "bolt-str.h"
#include "bolt-unix.h"
typedef enum GuardState {
GUARD_STATE_ACTIVE = 0,
GUARD_STATE_RELEASED = 1
} GuardState;
/* BoltGuard */
static void bolt_guard_remove (BoltGuard *guard);
struct _BoltGuard
{
GObject object;
/* book-keeping */
GuardState state;
char *path;
char *fifo;
guint watch;
/* properties */
char *id;
char *who;
pid_t pid;
};
enum {
PROP_GUARD_0,
PROP_PATH,
PROP_FIFO,
PROP_ID,
PROP_WHO,
PROP_PID,
PROP_GUARD_LAST
};
static GParamSpec *guard_props[PROP_GUARD_LAST] = { NULL, };
enum {
GUARD_SIGNAL_RELEASED,
SIGNAL_LAST
};
static guint guard_signals[SIGNAL_LAST] = {0};
G_DEFINE_TYPE (BoltGuard,
bolt_guard,
G_TYPE_OBJECT);
static void
bolt_guard_dispose (GObject *object)
{
BoltGuard *guard = BOLT_GUARD (object);
/* remove our state file */
bolt_guard_remove (guard);
if (guard->state != GUARD_STATE_RELEASED)
{
/* signal to clients to that we have been released.
* NB: we must be intact for method call */
guard->state = GUARD_STATE_RELEASED;
g_signal_emit (object, guard_signals[GUARD_SIGNAL_RELEASED], 0);
}
G_OBJECT_CLASS (bolt_guard_parent_class)->dispose (object);
}
static void
bolt_guard_finalize (GObject *object)
{
BoltGuard *guard = BOLT_GUARD (object);
if (guard->watch)
g_source_remove (guard->watch);
g_clear_pointer (&guard->path, g_free);
g_clear_pointer (&guard->fifo, g_free);
g_clear_pointer (&guard->who, g_free);
g_clear_pointer (&guard->id, g_free);
G_OBJECT_CLASS (bolt_guard_parent_class)->finalize (object);
}
static void
bolt_guard_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltGuard *guard = BOLT_GUARD (object);
switch (prop_id)
{
case PROP_PATH:
g_value_set_string (value, guard->path);
break;
case PROP_FIFO:
g_value_set_string (value, guard->fifo);
break;
case PROP_ID:
g_value_set_string (value, guard->id);
break;
case PROP_WHO:
g_value_set_string (value, guard->who);
break;
case PROP_PID:
g_value_set_ulong (value, guard->pid);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_guard_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltGuard *guard = BOLT_GUARD (object);
switch (prop_id)
{
case PROP_PATH:
guard->path = g_value_dup_string (value);
break;
case PROP_FIFO:
guard->fifo = g_value_dup_string (value);
break;
case PROP_ID:
guard->id = g_value_dup_string (value);
break;
case PROP_WHO:
guard->who = g_value_dup_string (value);
break;
case PROP_PID:
guard->pid = g_value_get_ulong (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_guard_init (BoltGuard *guard)
{
guard->state = GUARD_STATE_ACTIVE;
}
static void
bolt_guard_class_init (BoltGuardClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = bolt_guard_dispose;
gobject_class->finalize = bolt_guard_finalize;
gobject_class->get_property = bolt_guard_get_property;
gobject_class->set_property = bolt_guard_set_property;
guard_props[PROP_PATH] =
g_param_spec_string ("path",
NULL, NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
guard_props[PROP_FIFO] =
g_param_spec_string ("fifo",
NULL, NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
guard_props[PROP_ID] =
g_param_spec_string ("id",
NULL, NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
guard_props[PROP_WHO] =
g_param_spec_string ("who",
NULL, NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
guard_props[PROP_PID] =
g_param_spec_ulong ("pid",
NULL, NULL,
0, G_MAXULONG, 0,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_GUARD_LAST,
guard_props);
guard_signals[GUARD_SIGNAL_RELEASED] =
g_signal_new ("released",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
0);
}
static void
bolt_guard_remove (BoltGuard *guard)
{
g_autoptr(GError) err = NULL;
gboolean ok;
/* we are not saved */
if (guard->path == NULL)
return;
/* */
if (guard->fifo != NULL)
{
bolt_debug (LOG_TOPIC ("guard"),
"not removing guard '%s' with active fifo",
guard->id);
return;
}
ok = bolt_unlink (guard->path, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("guard"),
"Could not remove power guard: '%s' @ %s",
guard->id, guard->path);
return;
}
g_clear_pointer (&guard->path, g_free);
g_object_notify_by_pspec (G_OBJECT (guard),
guard_props[PROP_PATH]);
}
static void
bolt_guard_fifo_cleanup (BoltGuard *guard)
{
g_autoptr(GError) err = NULL;
gboolean ok;
if (guard->fifo == NULL)
return;
ok = bolt_unlink (guard->fifo, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("guard"),
"Could not remove FIFO for power guard: '%s' @ %s",
guard->id, guard->fifo);
}
g_clear_pointer (&guard->fifo, g_free);
g_object_notify_by_pspec (G_OBJECT (guard), guard_props[PROP_FIFO]);
}
static gboolean
bolt_guard_mkfifo (BoltGuard *guard,
GError **error)
{
g_autoptr(GError) err = NULL;
int r;
g_return_val_if_fail (BOLT_IS_GUARD (guard), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (guard->fifo == NULL)
guard->fifo = g_strdup_printf ("%s.fifo", guard->path);
r = bolt_mkfifo (guard->fifo, 0600, &err);
if (r == -1 && !bolt_err_exists (err))
return bolt_error_propagate (error, &err);
g_object_notify_by_pspec (G_OBJECT (guard),
guard_props[PROP_FIFO]);
return TRUE;
}
static gboolean
guard_has_event (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
BoltGuard *guard = data;
bolt_info (LOG_TOPIC ("guard"), "got event for guard '%s' (%x)",
guard->id, (guint) condition);
guard->watch = 0;
return FALSE;
}
static void
guard_watch_release (gpointer data)
{
BoltGuard *guard = data;
if (guard->state == GUARD_STATE_RELEASED)
/* if we are already released, the FIFO was kept
* alive on purpose, so do nothing here */
return;
if (guard->fifo == NULL)
{
bolt_bug (LOG_TOPIC ("guard"), "FIFO event but no FIFO");
return;
}
bolt_guard_fifo_cleanup (guard);
bolt_debug (LOG_TOPIC ("guard"),
"released watch reference for guard '%s'",
guard->id);
g_object_unref (guard);
}
int
bolt_guard_monitor (BoltGuard *guard,
GError **error)
{
g_autoptr(GIOChannel) ch = NULL;
gboolean ok;
int fd;
g_return_val_if_fail (BOLT_IS_GUARD (guard), -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
ok = bolt_guard_mkfifo (guard, error);
if (!ok)
return -1;
/* reader */
fd = bolt_open (guard->fifo, O_RDONLY | O_CLOEXEC | O_NONBLOCK, 0, error);
if (fd == -1)
return -1;
ch = g_io_channel_unix_new (fd);
g_io_channel_set_close_on_unref (ch, TRUE);
g_io_channel_set_encoding (ch, NULL, NULL);
g_io_channel_set_buffered (ch, FALSE);
g_io_channel_set_flags (ch, G_IO_FLAG_NONBLOCK, NULL);
fd = -1; /* the GIOChannel owns the fd, via _close_on_unref () */
(void) fd; /* The above is an intentional dead store */
/* writer */
fd = bolt_open (guard->fifo, O_WRONLY | O_CLOEXEC | O_NONBLOCK, 0, error);
if (fd == -1)
return -1;
/* NB: we take a ref to the guard here */
guard->watch = g_io_add_watch_full (ch,
G_PRIORITY_DEFAULT,
G_IO_HUP | G_IO_ERR,
guard_has_event,
g_object_ref (guard),
guard_watch_release);
return fd;
}
const char *
bolt_guard_get_id (BoltGuard *guard)
{
g_return_val_if_fail (BOLT_IS_GUARD (guard), NULL);
return guard->id;
}
const char *
bolt_guard_get_who (BoltGuard *guard)
{
g_return_val_if_fail (BOLT_IS_GUARD (guard), NULL);
return guard->who;
}
guint
bolt_guard_get_pid (BoltGuard *guard)
{
g_return_val_if_fail (BOLT_IS_GUARD (guard), 0);
return (guint) guard->pid;
}
const char *
bolt_guard_get_path (BoltGuard *guard)
{
g_return_val_if_fail (BOLT_IS_GUARD (guard), NULL);
return guard->path;
}
const char *
bolt_guard_get_fifo (BoltGuard *guard)
{
g_return_val_if_fail (BOLT_IS_GUARD (guard), NULL);
return guard->fifo;
}
GPtrArray *
bolt_guard_recover (const char *statedir,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(GDir) dir = NULL;
g_autoptr(GPtrArray) guards = NULL;
const char *name;
g_return_val_if_fail (statedir != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
dir = g_dir_open (statedir, 0, error);
if (dir == NULL)
return NULL;
guards = g_ptr_array_new_with_free_func (g_object_unref);
while ((name = g_dir_read_name (dir)) != NULL)
{
g_autoptr(BoltGuard) guard = NULL;
int fd;
if (!g_str_has_suffix (name, ".guard"))
continue;
guard = bolt_guard_load (statedir, name, &err);
if (guard == NULL)
{
bolt_warn_err (err, LOG_TOPIC ("guard"),
"could not load guard '%s'", name);
g_clear_error (&err);
continue;
}
/* internal guards are discarded */
if (guard->fifo == NULL)
{
bolt_info (LOG_TOPIC ("guard"),
"ignoring guard '%s' for '%s': no fifo",
guard->id, guard->who);
continue;
}
else if (!bolt_pid_is_alive (guard->pid))
{
bolt_info (LOG_TOPIC ("guard"),
"ignoring guard '%s' for '%s': process dead",
guard->id, guard->who);
bolt_guard_fifo_cleanup (guard);
continue;
}
fd = bolt_guard_monitor (guard, &err);
if (fd < 0)
{
bolt_warn_err (err, "could not monitor guard '%d'",
guard->id);
g_clear_error (&err);
continue;
}
/* close the write side */
(void) close (fd);
/* monitoring adds a reference that we don't want */
g_object_unref (guard);
g_ptr_array_add (guards, guard);
guard = NULL;
}
return g_steal_pointer (&guards);
}
gboolean
bolt_guard_save (BoltGuard *guard,
GFile *guarddir,
GError **error)
{
g_autoptr(GFile) guardfile = NULL;
g_autoptr(GKeyFile) kf = NULL;
g_autofree char *path = NULL;
g_autofree char *name = NULL;
gboolean ok;
g_return_val_if_fail (guard->path == NULL, FALSE);
name = g_strdup_printf ("%s.guard", guard->id);
guardfile = g_file_get_child (guarddir, name);
path = g_file_get_path (guardfile);
kf = g_key_file_new ();
g_key_file_set_string (kf, "guard", "id", guard->id);
g_key_file_set_string (kf, "guard", "who", guard->who);
g_key_file_set_uint64 (kf, "guard", "pid", guard->pid);
ok = g_key_file_save_to_file (kf, path, error);
if (ok)
{
guard->path = g_steal_pointer (&path);
g_object_notify_by_pspec (G_OBJECT (guard),
guard_props[PROP_PATH]);
}
return ok;
}
BoltGuard *
bolt_guard_load (const char *statedir,
const char *name,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(GKeyFile) kf = NULL;
g_autofree char *path = NULL;
g_autofree char *who = NULL;
g_autofree char *id = NULL;
g_autofree char *fifo = NULL;
gboolean ok;
gulong pid;
path = g_build_filename (statedir, name, NULL);
kf = g_key_file_new ();
ok = g_key_file_load_from_file (kf, path, 0, error);
if (!ok)
return NULL;
id = g_key_file_get_string (kf, "guard", "id", &err);
if (id == NULL)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"could not read 'id' field: %s", err->message);
return NULL;
}
who = g_key_file_get_string (kf, "guard", "who", &err);
if (who == NULL)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"field missing ('who')");
return NULL;
}
pid = (gulong) g_key_file_get_uint64 (kf, "guard", "pid", &err);
if (err != NULL)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"field missing ('pid')");
return NULL;
}
fifo = g_strdup_printf ("%s.fifo", path);
if (!g_file_test (fifo, G_FILE_TEST_EXISTS))
g_clear_pointer (&fifo, g_free);
return g_object_new (BOLT_TYPE_GUARD,
"path", path,
"fifo", fifo,
"id", id,
"who", who,
"pid", pid,
NULL);
}
bolt-0.9.2/boltd/bolt-guard.h 0000664 0000000 0000000 00000003552 14174530510 0016004 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2020 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-exported.h"
#include
G_BEGIN_DECLS
#define BOLT_TYPE_GUARD bolt_guard_get_type ()
G_DECLARE_FINAL_TYPE (BoltGuard, bolt_guard, BOLT, GUARD, GObject);
int bolt_guard_monitor (BoltGuard *guard,
GError **error);
const char * bolt_guard_get_id (BoltGuard *guard);
const char * bolt_guard_get_who (BoltGuard *guard);
guint bolt_guard_get_pid (BoltGuard *guard);
const char * bolt_guard_get_path (BoltGuard *guard);
const char * bolt_guard_get_fifo (BoltGuard *guard);
GPtrArray * bolt_guard_recover (const char *statedir,
GError **error);
gboolean bolt_guard_save (BoltGuard *guard,
GFile *guarddir,
GError **error);
BoltGuard * bolt_guard_load (const char *statedir,
const char *name,
GError **error);
G_END_DECLS
bolt-0.9.2/boltd/bolt-journal.c 0000664 0000000 0000000 00000034262 14174530510 0016351 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-journal.h"
#include "bolt-error.h"
#include "bolt-fs.h"
#include "bolt-io.h"
#include "bolt-log.h"
#include "bolt-macros.h"
#include "bolt-str.h"
#include
#include
#include
/* ************************************ */
/* BoltJournal */
static void bolt_journal_initable_iface_init (GInitableIface *iface);
static gboolean bolt_journal_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error);
struct _BoltJournal
{
GObject object;
GFile *root;
char *name;
GFile *path;
gboolean fresh;
int fd;
/* serials */
gint64 sl_time;
guint32 sl_count;
};
enum {
PROP_JOURNAL_0,
PROP_ROOT,
PROP_NAME,
PROP_FRESH,
PROP_JOURNAL_LAST
};
static GParamSpec *journal_props[PROP_JOURNAL_LAST] = { NULL, };
G_DEFINE_TYPE_WITH_CODE (BoltJournal,
bolt_journal,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
bolt_journal_initable_iface_init));
static void
bolt_journal_finalize (GObject *object)
{
BoltJournal *journal = BOLT_JOURNAL (object);
if (journal->fd > -1)
bolt_close (journal->fd, NULL);
g_clear_object (&journal->root);
g_clear_pointer (&journal->name, g_free);
g_clear_object (&journal->path);
G_OBJECT_CLASS (bolt_journal_parent_class)->finalize (object);
}
static void
bolt_journal_init (BoltJournal *journal)
{
journal->fd = -1;
}
static void
bolt_journal_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltJournal *journal = BOLT_JOURNAL (object);
switch (prop_id)
{
case PROP_ROOT:
g_value_set_object (value, journal->root);
break;
case PROP_NAME:
g_value_set_string (value, journal->name);
break;
case PROP_FRESH:
g_value_set_boolean (value, journal->fresh);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_journal_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltJournal *journal = BOLT_JOURNAL (object);
switch (prop_id)
{
case PROP_ROOT:
journal->root = g_value_dup_object (value);
break;
case PROP_NAME:
journal->name = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_journal_class_init (BoltJournalClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = bolt_journal_finalize;
gobject_class->get_property = bolt_journal_get_property;
gobject_class->set_property = bolt_journal_set_property;
journal_props[PROP_ROOT] =
g_param_spec_object ("root",
NULL, NULL,
G_TYPE_FILE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME);
journal_props[PROP_NAME] =
g_param_spec_string ("name", NULL, NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME);
journal_props[PROP_FRESH] =
g_param_spec_boolean ("fresh", NULL, NULL,
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_JOURNAL_LAST,
journal_props);
}
static void
bolt_journal_initable_iface_init (GInitableIface *iface)
{
iface->init = bolt_journal_initialize;
}
static gboolean
bolt_journal_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *path = NULL;
g_autofree char *size = NULL;
bolt_autoclose int fd = -1;
struct stat st;
BoltJournal *journal;
gboolean ok;
journal = BOLT_JOURNAL (initable);
if (bolt_strzero (journal->name) || journal->root == NULL)
{
bolt_bug ("invalid arguments");
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"root and/or name NULL for journal");
return FALSE;
}
journal->path = g_file_get_child (journal->root, journal->name);
ok = bolt_fs_make_parent_dirs (journal->path, &err);
if (!ok && !bolt_err_exists (err))
return bolt_error_propagate (error, &err);
path = g_file_get_path (journal->path);
fd = bolt_open (path,
O_RDWR | O_APPEND | O_CREAT | O_CLOEXEC,
0666,
error);
if (fd < 0)
return FALSE;
memset (&st, 0, sizeof (st));
ok = bolt_fstat (fd, &st, &err);
if (!ok)
{
g_propagate_prefixed_error (error, g_steal_pointer (&err),
"could not read from journal: ");
return FALSE;
}
size = g_format_size ((guint64) st.st_size);
bolt_info (LOG_TOPIC ("journal"), "opened for '%.13s'; size: %s",
journal->name, size);
journal->fresh = st.st_size == 0;
journal->fd = bolt_steal (&fd, -1);
bolt_debug (LOG_TOPIC ("journal"), "fresh: %s, fd: %d",
bolt_yesno (journal->fresh), journal->fd);
return TRUE;
}
/* internal methods */
static gboolean
bolt_journal_write_entry (int fd,
const char *id,
BoltJournalOp op,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *data = NULL;
const char *opstr;
gboolean ok;
guint64 now;
size_t l;
g_return_val_if_fail (fd > -1, FALSE);
now = (guint64) g_get_real_time ();
opstr = bolt_journal_op_to_string (op);
data = g_strdup_printf ("%s %s %016"G_GINT64_MODIFIER "X\n",
id, opstr, now);
l = strlen (data);
ok = bolt_write_all (fd, data, l, &err);
if (!ok)
{
g_propagate_prefixed_error (error, g_steal_pointer (&err),
"could not add journal entry: ");
return FALSE;
}
bolt_debug (LOG_TOPIC ("journal"), "wrote '%.*s' to %d",
l - 1, data, fd);
return TRUE;
}
static void
bolt_journal_set_fresh (BoltJournal *journal,
gboolean fresh)
{
if (journal->fresh == fresh)
return;
journal->fresh = fresh;
g_object_notify_by_pspec (G_OBJECT (journal),
journal_props[PROP_FRESH]);
}
/* public methods */
BoltJournal *
bolt_journal_new (GFile *root,
const char *name,
GError **error)
{
return g_initable_new (BOLT_TYPE_JOURNAL,
NULL, error,
"root", root,
"name", name,
NULL);
}
gboolean
bolt_journal_is_fresh (BoltJournal *journal)
{
g_return_val_if_fail (BOLT_IS_JOURNAL (journal), FALSE);
return journal->fresh;
}
gboolean
bolt_journal_put (BoltJournal *journal,
const char *id,
BoltJournalOp op,
GError **error)
{
gboolean ok;
int r;
g_return_val_if_fail (BOLT_IS_JOURNAL (journal), FALSE);
g_return_val_if_fail (id != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ok = bolt_journal_write_entry (journal->fd, id, op, error);
if (!ok)
return FALSE;
r = fdatasync (journal->fd);
if (r == -1)
{
bolt_warn (LOG_TOPIC ("journal"),
"could not flush (fdatasync) journal: %s",
g_strerror (errno));
}
bolt_journal_set_fresh (journal, FALSE);
return TRUE;
}
gboolean
bolt_journal_put_diff (BoltJournal *journal,
GHashTable *diff,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *path = NULL;
g_autofree char *base = NULL;
bolt_autoclose int fd = -1;
struct stat st;
GHashTableIter iter;
gpointer key, val;
gboolean ok = TRUE;
g_return_val_if_fail (BOLT_IS_JOURNAL (journal), FALSE);
g_return_val_if_fail (diff != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
base = g_file_get_path (journal->path);
path = g_strdup_printf ("%s.lock", base);
fd = bolt_open (path,
O_RDWR | O_CREAT | O_CLOEXEC | O_TRUNC,
0666,
error);
if (fd < 0)
return FALSE;
memset (&st, 0, sizeof (st));
ok = bolt_fstat (journal->fd, &st, &err);
if (!ok)
{
g_propagate_prefixed_error (error, g_steal_pointer (&err),
"could not query journal: ");
return FALSE;
}
ok = bolt_lseek (journal->fd, 0, SEEK_SET, NULL, error);
if (ok)
ok = bolt_copy_bytes (journal->fd, fd, st.st_size, error);
g_hash_table_iter_init (&iter, diff);
while (ok && g_hash_table_iter_next (&iter, &key, &val))
{
const char *uid = key;
const int opcode = GPOINTER_TO_INT (val);
BoltJournalOp op;
switch (opcode)
{
case '+':
op = BOLT_JOURNAL_ADDED;
break;
case '-':
op = BOLT_JOURNAL_REMOVED;
break;
default:
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"unsupported op-code in diff: %c", opcode);
return FALSE;
}
ok = bolt_journal_write_entry (fd, uid, op, error);
}
if (ok)
ok = bolt_fdatasync (fd, error);
if (ok)
ok = bolt_faddflags (fd, O_APPEND, error);
if (ok)
ok = bolt_rename (path, base, error);
if (!ok)
return FALSE;
bolt_swap (journal->fd, fd);
bolt_journal_set_fresh (journal, FALSE);
return TRUE;
}
GPtrArray *
bolt_journal_list (BoltJournal *journal,
GError **error)
{
g_autoptr(GInputStream) is = NULL;
g_autoptr(GDataInputStream) ds = NULL;
GPtrArray *res = NULL;
off_t pos;
g_return_val_if_fail (BOLT_IS_JOURNAL (journal), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
pos = lseek (journal->fd, 0, SEEK_SET);
if (pos == (off_t) -1)
{
int code = errno;
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (code),
"could not read from journal: %s", g_strerror (code));
return NULL;
}
res = g_ptr_array_new_full (16, (GDestroyNotify) bolt_journal_item_free);
is = g_unix_input_stream_new (journal->fd, FALSE);
ds = g_data_input_stream_new (is);
g_return_val_if_fail (ds != NULL, res);
for (;; )
{
g_autoptr(GError) err = NULL;
g_autofree char *l = NULL;
g_autofree char *name = NULL;
g_autofree char *opstr = NULL;
BoltJournalItem *i;
BoltJournalOp op;
guint64 ts;
int n;
l = g_data_input_stream_read_line (ds, NULL, NULL, &err);
if (l == NULL)
{
if (err)
bolt_warn_err (err, LOG_TOPIC ("journal"),
"error reading from journal");
break;
}
n = sscanf (l, "%ms %ms %016" G_GINT64_MODIFIER "X",
&name, &opstr, &ts);
if (n != 3)
{
bolt_warn (LOG_TOPIC ("journal"), "invalid entry: '%s'", l);
continue;
}
op = bolt_journal_op_from_string (opstr, &err);
if (err != NULL)
{
bolt_warn_err (err, LOG_TOPIC ("journal"),
"skipping entry '%s'", l);
continue;
}
i = g_slice_new (BoltJournalItem);
i->id = g_strdup (name);
i->ts = ts;
i->op = op;
g_ptr_array_add (res, i);
}
return res;
}
gboolean
bolt_journal_reset (BoltJournal *journal,
GError **error)
{
gboolean ok;
g_return_val_if_fail (BOLT_IS_JOURNAL (journal), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ok = bolt_ftruncate (journal->fd, 0, error);
if (ok)
journal->fresh = TRUE;
return ok;
}
/* journal op methods */
const char *
bolt_journal_op_to_string (BoltJournalOp op)
{
switch (op)
{
case BOLT_JOURNAL_FAILED:
return "!";
case BOLT_JOURNAL_UNCHANGED:
return "=";
case BOLT_JOURNAL_ADDED:
return "+";
case BOLT_JOURNAL_REMOVED:
return "-";
}
bolt_warn_enum_unhandled (BoltJournalOp, op);
return "?";
}
BoltJournalOp
bolt_journal_op_from_string (const char *data,
GError **error)
{
g_return_val_if_fail (error == NULL || *error == NULL, BOLT_JOURNAL_FAILED);
/* both will be caught as errors after
* the switch statement */
if (data == NULL)
data = "";
else if (*data == '\0')
data = "";
switch (data[0])
{
case '!':
return BOLT_JOURNAL_FAILED;
case '+':
return BOLT_JOURNAL_ADDED;
case '-':
return BOLT_JOURNAL_REMOVED;
case '=':
return BOLT_JOURNAL_UNCHANGED;
}
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"invalid journal operation: %s", data);
return BOLT_JOURNAL_FAILED;
}
/*BoltJournalItem */
void
bolt_journal_item_free (BoltJournalItem *entry)
{
g_free (entry->id);
g_slice_free (BoltJournalItem, entry);
}
bolt-0.9.2/boltd/bolt-journal.h 0000664 0000000 0000000 00000004727 14174530510 0016361 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include
/* BoltJournal - database for devices, keys */
#define BOLT_TYPE_JOURNAL bolt_journal_get_type ()
G_DECLARE_FINAL_TYPE (BoltJournal, bolt_journal, BOLT, JOURNAL, GObject);
typedef enum BoltJournalOp {
BOLT_JOURNAL_FAILED = -1,
BOLT_JOURNAL_UNCHANGED = '=',
BOLT_JOURNAL_ADDED = '+',
BOLT_JOURNAL_REMOVED = '-',
} BoltJournalOp;
typedef struct BoltJournalItem
{
char *id;
BoltJournalOp op;
guint64 ts; /* timestamp */
} BoltJournalItem;
BoltJournal * bolt_journal_new (GFile *root,
const char *name,
GError **error);
gboolean bolt_journal_is_fresh (BoltJournal *journal);
gboolean bolt_journal_put (BoltJournal *journal,
const char *id,
BoltJournalOp op,
GError **error);
gboolean bolt_journal_put_diff (BoltJournal *journal,
GHashTable *diff,
GError **error);
GPtrArray * bolt_journal_list (BoltJournal *journal,
GError **error);
gboolean bolt_journal_reset (BoltJournal *journal,
GError **error);
/* BoltJournalOp */
const char * bolt_journal_op_to_string (BoltJournalOp op);
BoltJournalOp bolt_journal_op_from_string (const char *data,
GError **error);
/* BoltJournalItem */
void bolt_journal_item_free (BoltJournalItem *entry);
bolt-0.9.2/boltd/bolt-key.c 0000664 0000000 0000000 00000015130 14174530510 0015460 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-store.h"
#include "bolt-error.h"
#include "bolt-fs.h"
#include "bolt-io.h"
#include "bolt-key.h"
#include "bolt-rnd.h"
#include "bolt-str.h"
#include
#include
/* ************************************ */
/* BoltKey */
struct _BoltKey
{
GObject object;
/* the actual key plus the null char */
char data[BOLT_KEY_CHARS + 1];
gboolean fresh;
};
enum {
PROP_KEY_0,
PROP_KEY_FRESH,
PROP_KEY_LAST
};
static GParamSpec *key_props[PROP_KEY_LAST] = { NULL, };
G_DEFINE_TYPE (BoltKey,
bolt_key,
G_TYPE_OBJECT);
static void
bolt_key_finalize (GObject *object)
{
BoltKey *key = BOLT_KEY (object);
bolt_erase_n (key->data, sizeof (key->data));
G_OBJECT_CLASS (bolt_key_parent_class)->finalize (object);
}
static void
bolt_key_init (BoltKey *key)
{
memset (key->data, 0, sizeof (key->data));
}
static void
bolt_key_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltKey *key = BOLT_KEY (object);
switch (prop_id)
{
case PROP_KEY_FRESH:
g_value_set_boolean (value, key->fresh);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_key_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltKey *key = BOLT_KEY (object);
switch (prop_id)
{
case PROP_KEY_FRESH:
key->fresh = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_key_class_init (BoltKeyClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = bolt_key_finalize;
gobject_class->get_property = bolt_key_get_property;
gobject_class->set_property = bolt_key_set_property;
key_props[PROP_KEY_FRESH] =
g_param_spec_boolean ("fresh",
NULL, NULL,
FALSE,
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_READWRITE |
G_PARAM_STATIC_NICK);
g_object_class_install_properties (gobject_class,
PROP_KEY_LAST,
key_props);
}
/* internal methods */
/* public methods */
BoltKey *
bolt_key_new (GError **error)
{
BoltKey *key;
BoltRng rng;
char data[BOLT_KEY_BYTES];
rng = bolt_get_random_data (data, BOLT_KEY_BYTES);
/* fail if we can not be sure that we have good enough
* random data, which is only guaranteed by getrandom */
if (rng != BOLT_RNG_GETRANDOM)
{
g_set_error (error, BOLT_ERROR_FAILED, BOLT_ERROR_NOKEY,
"failed to create key: no random data");
return NULL;
}
key = g_object_new (BOLT_TYPE_KEY, NULL);
for (guint i = 0; i < BOLT_KEY_BYTES; i++)
{
char *pos = key->data + 2 * i;
gulong n = sizeof (key->data) - 2 * i;
g_snprintf (pos, n, "%02hhx", data[i]);
}
bolt_erase_n (data, sizeof (data));
key->fresh = TRUE;
return key;
}
gboolean
bolt_key_write_to (BoltKey *key,
int fd,
BoltSecurity *level,
GError **error)
{
g_autoptr(GError) err = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_KEY (key), FALSE);
g_return_val_if_fail (fd > -1, FALSE);
g_return_val_if_fail (level != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
*level = BOLT_SECURITY_USER;
if (key->data[0] == '\0')
return TRUE;
ok = bolt_write_all (fd, key->data, BOLT_KEY_CHARS, &err);
if (!ok && g_error_matches (err, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_BADKEY, "invalid key data");
else if (!ok)
bolt_error_propagate (error, &err);
else if (!key->fresh) /* ok == True */
*level = BOLT_SECURITY_SECURE;
return ok;
}
gboolean
bolt_key_save_file (BoltKey *key,
GFile *file,
GError **error)
{
gboolean ok;
ok = g_file_replace_contents (file,
key->data, BOLT_KEY_CHARS,
NULL, FALSE,
G_FILE_CREATE_PRIVATE,
NULL,
NULL, error);
return ok;
}
BoltKey *
bolt_key_load_file (GFile *file,
GError **error)
{
g_autoptr(BoltKey) key = NULL;
g_autofree char *path = NULL;
gboolean ok;
gsize len;
int fd;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
key = g_object_new (BOLT_TYPE_KEY, NULL);
path = g_file_get_path (file);
fd = bolt_open (path, O_CLOEXEC | O_RDONLY, 0, error);
if (fd < 0)
return NULL;
memset (key->data, 0, sizeof (key->data));
ok = bolt_read_all (fd, key->data, BOLT_KEY_CHARS, &len, error);
close (fd);
if (!ok)
return NULL;
/* empty key; NB: the kernel gives us "\n" for an empty key */
if (len == 0 || (len == 1 && g_ascii_isspace (key->data[0])))
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_NOKEY,
"key-file exists but contains no data");
return NULL;
}
if (len != BOLT_KEY_CHARS)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_BADKEY,
"unexpected key size (corrupt key?): %zu", len);
return NULL;
}
key->fresh = FALSE;
return g_steal_pointer (&key);
}
BoltKeyState
bolt_key_get_state (BoltKey *key)
{
if (key == NULL)
return BOLT_KEY_MISSING;
g_return_val_if_fail (BOLT_IS_KEY (key), BOLT_KEY_UNKNOWN);
return key->fresh ? BOLT_KEY_NEW : BOLT_KEY_HAVE;
}
bolt-0.9.2/boltd/bolt-key.h 0000664 0000000 0000000 00000003232 14174530510 0015465 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include "bolt-enums.h"
G_BEGIN_DECLS
/* BoltKey - represents a key to authorize devices with */
#define BOLT_TYPE_KEY bolt_key_get_type ()
G_DECLARE_FINAL_TYPE (BoltKey, bolt_key, BOLT, KEY, GObject);
#define BOLT_KEY_BYTES 32
#define BOLT_KEY_CHARS 64
BoltKey * bolt_key_new (GError **error);
gboolean bolt_key_write_to (BoltKey *key,
int fd,
BoltSecurity *level,
GError **error);
gboolean bolt_key_save_file (BoltKey *key,
GFile *file,
GError **error);
BoltKey * bolt_key_load_file (GFile *file,
GError **error);
BoltKeyState bolt_key_get_state (BoltKey *key);
G_END_DECLS
bolt-0.9.2/boltd/bolt-log.c 0000664 0000000 0000000 00000042156 14174530510 0015461 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-device.h"
#include "bolt-domain.h"
#include "bolt-error.h"
#include "bolt-macros.h"
#include "bolt-rnd.h"
#include "bolt-str.h"
#include "bolt-term.h"
#include "bolt-log.h"
#include
#include
#include
#include
#include
/* mapping taking from glib */
typedef struct BoltLogLevel
{
GLogLevelFlags code;
const char *prio;
const char *name;
} BoltLogLevel;
BoltLogLevel known_levels[] = {
{G_LOG_LEVEL_ERROR, "3", "error"},
{G_LOG_LEVEL_CRITICAL, "4", "critical"},
{G_LOG_LEVEL_WARNING, "4", "warning"},
{G_LOG_LEVEL_MESSAGE, "5", "message"},
{G_LOG_LEVEL_INFO, "6", "info"},
{G_LOG_LEVEL_DEBUG, "7", "debug"}
};
const char *
bolt_log_level_to_priority (GLogLevelFlags log_level)
{
for (gsize i = 0; i < G_N_ELEMENTS (known_levels); i++)
if (known_levels[i].code & log_level)
return known_levels[i].prio;
/* Default to LOG_NOTICE for custom log levels. */
return "5";
}
const char *
bolt_log_level_to_string (GLogLevelFlags log_level)
{
for (gsize i = 0; i < G_N_ELEMENTS (known_levels); i++)
if (known_levels[i].code & log_level)
return known_levels[i].name;
return "user";
}
static FILE *
log_level_to_file (GLogLevelFlags log_level)
{
return log_level & G_LOG_LEVEL_DEBUG ? stdout : stderr;
}
#define internal_error(fmt, ...) g_fprintf (stderr, "log-ERROR: " fmt "\n", __VA_ARGS__)
static const char *
bolt_color_for (FILE *out, const char *color)
{
int fd = fileno (out);
return g_log_writer_supports_color (fd) ? color : "";
}
struct _BoltLogCtx
{
BoltDevice *device;
const GError *error;
/* standard fields */
GLogField *self;
GLogField *message;
GLogField *priority;
GLogField *domain;
GLogField *topic;
gboolean is_bug;
/* field storage */
gsize n_fields;
GLogField fields[32];
/* heap or stack */
gboolean allocated;
};
static gboolean
bolt_log_ctx_next_field (BoltLogCtx *ctx, GLogField **next)
{
const gsize maxfields = G_N_ELEMENTS (ctx->fields);
gboolean res = TRUE;
gsize n; /* safe to use as index */
/* make sure we are ALWAYS within bounds */
n = MIN (ctx->n_fields, maxfields - 1);
*next = ctx->fields + n;
res = ctx->n_fields < maxfields;
ctx->n_fields += 1;
if (!res)
internal_error ("fields overflow. '%s' dropped",
ctx->fields[n].key);
return res;
}
#define BOLT_LOG_CTX_KEY "BOLT_LOG_CONTEXT"
static gsize
bolt_log_ctx_finish (BoltLogCtx *ctx)
{
GLogField *self;
gboolean ok;
ok = bolt_log_ctx_next_field (ctx, &self);
if (!ok)
{
const gsize maxfields = sizeof (ctx->fields);
gsize i = ctx->n_fields - maxfields;
ctx->n_fields = maxfields;
internal_error ("overflow of %" G_GSIZE_FORMAT, i);
}
self->key = BOLT_LOG_CTX_KEY;
self->value = ctx;
self->length = 0;
ctx->self = self;
return ctx->n_fields;
}
static gboolean
bolt_log_ctx_find_field (const BoltLogCtx *ctx,
const char *name,
const GLogField **out)
{
g_return_val_if_fail (ctx != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (out != NULL, FALSE);
for (gsize i = 0; i < ctx->n_fields; i++)
{
const GLogField *field = ctx->fields + i;
if (bolt_streq (field->key, name))
{
*out = field;
return TRUE;
}
}
return FALSE;
}
static void
handle_domain_field (BoltLogCtx *ctx,
const char *key,
gpointer ptr)
{
BoltDomain *dom = BOLT_DOMAIN (ptr);
const char *name;
GLogField *field;
g_return_if_fail (BOLT_IS_DOMAIN (dom));
bolt_log_ctx_next_field (ctx, &field);
field->key = BOLT_LOG_DOMAIN_UID;
field->value = bolt_domain_get_uid (dom);
field->length = -1;
name = bolt_domain_get_id (dom);
if (name == NULL)
return;
bolt_log_ctx_next_field (ctx, &field);
field->key = BOLT_LOG_DOMAIN_NAME;
field->value = name;
field->length = -1;
}
static void
handle_device_field (BoltLogCtx *ctx,
const char *key,
gpointer ptr)
{
BoltDevice *dev = BOLT_DEVICE (ptr);
BoltStatus status;
GLogField *field;
g_return_if_fail (BOLT_IS_DEVICE (dev));
ctx->device = dev;
bolt_log_ctx_next_field (ctx, &field);
field->key = BOLT_LOG_DEVICE_UID;
field->value = bolt_device_get_uid (dev);
field->length = -1;
bolt_log_ctx_next_field (ctx, &field);
field->key = BOLT_LOG_DEVICE_NAME;
field->value = bolt_device_get_name (dev);
field->length = -1;
bolt_log_ctx_next_field (ctx, &field);
status = bolt_device_get_status (dev);
field->key = BOLT_LOG_DEVICE_STATE;
field->value = bolt_status_to_string (status);
field->length = -1;
}
static void
handle_gerror_field (BoltLogCtx *ctx,
const char *key,
gpointer ptr)
{
const GError *error = ptr;
GLogField *field;
GError fallback = {
.domain = BOLT_ERROR,
.code = BOLT_ERROR_FAILED,
.message = (char *) "unknown cause",
};
ctx->error = error;
if (error == NULL)
{
error = &fallback;
ctx->is_bug = TRUE;
}
bolt_log_ctx_next_field (ctx, &field);
field->key = BOLT_LOG_ERROR_DOMAIN;
field->value = g_quark_to_string (error->domain);
field->length = -1;
bolt_log_ctx_next_field (ctx, &field);
field->key = BOLT_LOG_ERROR_CODE;
field->value = &error->code;
field->length = sizeof (gint);
bolt_log_ctx_next_field (ctx, &field);
field->key = BOLT_LOG_ERROR_MESSAGE;
field->value = error->message;
field->length = -1;
}
static void
handle_topic_field (BoltLogCtx *ctx,
const char *key,
gpointer ptr)
{
const char *value = ptr;
GLogField *field;
bolt_log_ctx_next_field (ctx, &field);
field->key = BOLT_LOG_TOPIC;
field->value = value;
field->length = -1;
ctx->topic = field;
if (bolt_streq ((const char *) ptr, "code"))
ctx->is_bug = TRUE;
}
struct SpecialField
{
const char *name;
void (*handler) (BoltLogCtx *ctx,
const char *key,
gpointer ptr);
} special_fields[] = {
{"device", handle_device_field},
{"domain", handle_domain_field},
{"error", handle_gerror_field},
{"topic", handle_topic_field}
};
static gboolean
handle_special_field (BoltLogCtx *ctx,
const char *key,
gpointer ptr)
{
gboolean handled = FALSE;
key++; /* remove the special key indicator */
for (gsize i = 0; i < G_N_ELEMENTS (special_fields); i++)
{
if (g_str_equal (key, special_fields[i].name))
{
special_fields[i].handler (ctx, key, ptr);
handled = TRUE;
}
}
return handled;
}
static gboolean
handle_passthrough_field (BoltLogCtx *ctx,
const char *key,
const char *val)
{
GLogField *field;
key++; /* remove the pass-through key indicator */
bolt_log_ctx_next_field (ctx, &field);
field->key = key;
field->value = val;
field->length = -1;
return TRUE;
}
static void
add_bug_marker (BoltLogCtx *ctx)
{
GLogField *field;
bolt_log_ctx_next_field (ctx, &field);
field->key = BOLT_LOG_BUG_MARK;
field->value = "*";
field->length = -1;
}
void
bolt_log (const char *domain,
GLogLevelFlags level,
...)
{
va_list args;
va_start (args, level);
bolt_logv (domain, level, args);
va_end (args);
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
void
bolt_logv (const char *domain,
GLogLevelFlags level,
va_list args)
{
BoltLogCtx ctx = {NULL, };
char message[1024] = {0, };
const char *key;
bolt_log_ctx_next_field (&ctx, &ctx.message);
bolt_log_ctx_next_field (&ctx, &ctx.priority);
bolt_log_ctx_next_field (&ctx, &ctx.domain);
while ((key = va_arg (args, const char *)) != NULL)
{
gboolean handled;
if (*key == LOG_SPECIAL_CHAR)
{
gpointer ptr = va_arg (args, gpointer);
handled = handle_special_field (&ctx, key, ptr);
}
else if (*key == LOG_PASSTHROUGH_CHAR)
{
const char *val = va_arg (args, const char *);
handled = handle_passthrough_field (&ctx, key, val);
}
else
{
break;
}
if (!handled)
internal_error ("unknown field: %s", key);
}
g_vsnprintf (message, sizeof (message), key ? : "", args);
ctx.message->key = "MESSAGE";
ctx.message->value = message;
ctx.message->length = -1;
ctx.priority->key = "PRIORITY";
ctx.priority->value = bolt_log_level_to_priority (level);
ctx.priority->length = -1;
ctx.domain->key = "GLIB_DOMAIN";
ctx.domain->value = domain ? : "boltd";
ctx.domain->length = -1;
if (ctx.is_bug)
add_bug_marker (&ctx);
bolt_log_ctx_finish (&ctx);
/* pass it to the normal log mechanisms;
* this should handle aborting on fatal
* error messages for us. */
g_log_structured_array (level, ctx.fields, ctx.n_fields);
}
#pragma GCC diagnostic pop
static char *
format_uid_name (const char *uid,
const char *name,
char *buffer,
gsize len,
int size)
{
static const int u = 13;
int n;
n = MAX (0, size - u);
g_snprintf (buffer, len, "%-.*s-%-*.*s", u, uid, n, n, name);
return buffer;
}
static char *
format_device_id (BoltDevice *device, char *buffer, gsize len, int size)
{
const char *uid;
const char *name;
uid = bolt_device_get_uid (device);
name = bolt_device_get_name (device);
format_uid_name (uid, name, buffer, len, size);
return buffer;
}
#define TIME_MAXFMT 255
GLogWriterOutput
bolt_log_stdstream (const BoltLogCtx *ctx,
GLogLevelFlags log_level,
guint flags)
{
FILE *out = log_level_to_file (log_level);
const char *normal = bolt_color_for (out, ANSI_NORMAL);
const char *gray = bolt_color_for (out, ANSI_HIGHLIGHT_BLACK);
const char *blue = bolt_color_for (out, ANSI_BLUE);
const char *fg = normal;
const char *message;
const GLogField *f;
char the_time[TIME_MAXFMT];
struct tm lt;
struct tm *tm;
time_t now;
g_return_val_if_fail (ctx != NULL, G_LOG_WRITER_UNHANDLED);
g_return_val_if_fail (ctx->message != NULL, G_LOG_WRITER_UNHANDLED);
time (&now);
tm = localtime_r (&now, <);
if (tm && strftime (the_time, sizeof (the_time), "%T", tm) > 0)
g_fprintf (out, "%s%s%s ", gray, the_time, normal);
if (log_level == G_LOG_LEVEL_CRITICAL ||
log_level == G_LOG_LEVEL_ERROR)
fg = bolt_color_for (out, ANSI_RED);
else if (log_level == G_LOG_LEVEL_WARNING)
fg = bolt_color_for (out, ANSI_YELLOW);
else if (log_level == G_LOG_LEVEL_DEBUG)
fg = gray;
if (ctx->device)
{
char name[64];
format_device_id (ctx->device, name, sizeof (name), 30);
g_fprintf (out, "[%s%s%s] ", blue, name, normal);
}
else if (bolt_log_ctx_find_field (ctx, BOLT_LOG_DOMAIN_UID, &f))
{
const char *uid = f->value;
const char *name = "domain?";
char ident[64];
if (bolt_log_ctx_find_field (ctx, BOLT_LOG_DOMAIN_NAME, &f))
name = f->value;
format_uid_name (uid, name, ident, sizeof (ident), 30);
g_fprintf (out, "[%s%s%s] ", blue, ident, fg);
}
else if (bolt_log_ctx_find_field (ctx, BOLT_LOG_DEVICE_UID, &f))
{
const char *uid = f->value;
g_fprintf (out, "[%s%.13s %17s%s] ", blue, uid, " ", fg);
}
if (ctx->topic)
g_fprintf (out, "%s%s%s: ", blue, (const char *) ctx->topic->value, fg);
message = ctx->message->value;
g_fprintf (out, "%s%s%s", fg, message, normal);
if (ctx->error)
{
const char *yellow = bolt_color_for (out, ANSI_YELLOW);
const char *msg = ctx->error->message;
if (strlen (message) == 0)
{
const char *lvl = bolt_log_level_to_string (log_level);
g_fprintf (out, "%s%s%s", fg, lvl, normal);
}
g_fprintf (out, ": %s%s%s", yellow, msg, normal);
}
g_fprintf (out, "\n");
fflush (out);
return G_LOG_WRITER_HANDLED;
}
static int
bolt_cat_printf (char **buffer, gsize *size, const char *fmt, ...)
{
va_list args;
char *p = *buffer;
gsize s = *size;
int n;
va_start (args, fmt);
n = g_vsnprintf (p, s, fmt, args);
va_end (args);
/* TODO: check n */
*size = s - n;
*buffer = p + n;
return n;
}
void
bolt_log_fmt_journal (const BoltLogCtx *ctx,
GLogLevelFlags log_level,
char *message,
gsize size)
{
const char *m;
char *p = message;
const GLogField *f;
g_return_if_fail (ctx != NULL && ctx->message != NULL);
if (ctx->device)
{
char name[64];
format_device_id (ctx->device, name, sizeof (name), 40);
bolt_cat_printf (&p, &size, "[%s] ", name);
}
else if (bolt_log_ctx_find_field (ctx, BOLT_LOG_DOMAIN_UID, &f))
{
const char *uid = f->value;
const char *name = "domain?";
char ident[64];
if (bolt_log_ctx_find_field (ctx, BOLT_LOG_DOMAIN_NAME, &f))
name = f->value;
format_uid_name (uid, name, ident, sizeof (ident), 40);
bolt_cat_printf (&p, &size, "[%s] ", ident);
}
else if (bolt_log_ctx_find_field (ctx, BOLT_LOG_DEVICE_UID, &f))
{
const char *uid = f->value;
bolt_cat_printf (&p, &size, "[%.13s %27s] ", uid, " ");
}
if (ctx->topic)
bolt_cat_printf (&p, &size, "%s: ", (const char *) ctx->topic->value);
m = ctx->message->value;
bolt_cat_printf (&p, &size, "%s", m);
if (ctx->error)
{
const GError *error = ctx->error;
const char *msg = error->message;
if (strlen (m) == 0)
bolt_cat_printf (&p, &size, "%s", bolt_log_level_to_string (log_level));
bolt_cat_printf (&p, &size, ": %s", msg);
}
}
GLogWriterOutput
bolt_log_journal (const BoltLogCtx *ctx,
GLogLevelFlags log_level,
guint flags)
{
GLogWriterOutput res;
char message[2048];
GLogField msg = {"MESSAGE", message, -1};
g_return_val_if_fail (ctx != NULL, G_LOG_WRITER_UNHANDLED);
g_return_val_if_fail (ctx->message != NULL, G_LOG_WRITER_UNHANDLED);
bolt_log_fmt_journal (ctx, log_level, message, sizeof (message));
*ctx->message = msg;
res = g_log_writer_journald (log_level,
ctx->fields,
ctx->n_fields,
NULL);
return res;
}
BoltLogCtx *
bolt_log_ctx_acquire (const GLogField *fields,
gsize n)
{
BoltLogCtx *ctx;
if (bolt_streq (fields[n - 1].key, BOLT_LOG_CTX_KEY))
{
ctx = (BoltLogCtx *) fields[n - 1].value;
/* sanity check */
if (ctx->message == NULL)
return NULL;
return ctx;
}
ctx = g_new0 (BoltLogCtx, 1);
ctx->allocated = TRUE;
for (gsize i = 0; i < n; i++)
{
GLogField *field = (GLogField *) &fields[i];
if (bolt_streq (field->key, "MESSAGE"))
{
bolt_log_ctx_next_field (ctx, &ctx->message);
*(ctx->message) = *field;
}
else if (bolt_streq (field->key, "GLIB_DOMAIN"))
{
bolt_log_ctx_next_field (ctx, &ctx->domain);
*(ctx->domain) = *field;
}
else if (bolt_streq (field->key, "PRIORITY"))
{
bolt_log_ctx_next_field (ctx, &ctx->priority);
*(ctx->priority) = *field;
}
}
if (ctx->message == NULL)
{
g_free (ctx);
return NULL;
}
bolt_log_ctx_finish (ctx);
return ctx;
}
gboolean
bolt_log_ctx_set_id (BoltLogCtx *ctx,
const char *id)
{
if (ctx->self == NULL)
return FALSE;
else if (ctx->self->length != 0)
return FALSE;
ctx->self->value = id;
ctx->self->length = -1;
return TRUE;
}
void
bolt_log_ctx_free (BoltLogCtx *ctx)
{
if (ctx == NULL || ctx->allocated == FALSE)
return;
g_free (ctx);
}
const char *
blot_log_ctx_get_domain (BoltLogCtx *ctx)
{
return ctx->domain ? ctx->domain->value : NULL;
}
void
bolt_log_gen_id (char id[BOLT_LOG_MSG_IDLEN])
{
guint8 data[16] = {0, };
static const char ch[16] = "0123456789abcdef";
bolt_get_random_data (&data, sizeof (data));
for (guint i = 0; i < 16; i++)
{
const guint8 b = data[i];
g_assert_cmpint ((b >> 4), <, sizeof (ch));
id[i * 2] = ch[b >> 4];
id[i * 2 + 1] = ch[b & 15];
}
id[32] = '\0';
}
bolt-0.9.2/boltd/bolt-log.h 0000664 0000000 0000000 00000015717 14174530510 0015471 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-names.h"
#include
#include
G_BEGIN_DECLS
#define LOG_SPECIAL_CHAR '@'
#define LOG_PASSTHROUGH_CHAR '_'
#define LOG_DIRECT(k, v) "_" k, v
#define LOG_DEV(device) "@device", device
#define LOG_DOM(domain) "@domain", domain
#define LOG_ERR(error) "@error", error
#define LOG_TOPIC(topic) "@topic", topic
#define LOG_DOM_UID(uid) LOG_DIRECT (BOLT_LOG_DOMAIN_UID, uid)
#define LOG_DEV_UID(uid) LOG_DIRECT (BOLT_LOG_DEVICE_UID, uid)
#define LOG_MSG_ID(msg_id) LOG_DIRECT ("MESSAGE_ID", msg_id)
#define LOG_ID(id) LOG_MSG_ID (BOLT_LOG_MSG_ID_ ## id)
#define bolt_debug(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, \
LOG_DIRECT ("CODE_FILE", __FILE__), \
LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \
LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \
__VA_ARGS__)
#define bolt_info(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, \
LOG_DIRECT ("CODE_FILE", __FILE__), \
LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \
LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \
__VA_ARGS__)
#define bolt_msg(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, \
LOG_DIRECT ("CODE_FILE", __FILE__), \
LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \
LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \
__VA_ARGS__)
#define bolt_warn(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
LOG_DIRECT ("CODE_FILE", __FILE__), \
LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \
LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \
__VA_ARGS__)
#define bolt_critical(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
LOG_DIRECT ("CODE_FILE", __FILE__), \
LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \
LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \
__VA_ARGS__)
#define bolt_error(...) G_STMT_START { \
bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, \
LOG_DIRECT ("CODE_FILE", __FILE__), \
LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \
LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \
__VA_ARGS__); \
for (;; ) { } \
} G_STMT_END
#define bolt_warn_err(e, ...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
LOG_DIRECT ("CODE_FILE", __FILE__), \
LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \
LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \
LOG_ERR (e), \
__VA_ARGS__)
#define bolt_warn_enum_unhandled(the_enum, the_value) G_STMT_START { \
bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
LOG_DIRECT ("CODE_FILE", __FILE__), \
LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \
LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \
LOG_TOPIC ("code"), \
"unhandled value of enum " #the_enum "%d", \
(int) the_value); \
} G_STMT_END
#define bolt_bug(...) bolt_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
LOG_DIRECT ("CODE_FILE", __FILE__), \
LOG_DIRECT ("CODE_LINE", G_STRINGIFY (__LINE__)), \
LOG_DIRECT ("CODE_FUNC", G_STRFUNC), \
LOG_TOPIC ("code"), \
__VA_ARGS__)
void bolt_logv (const char *domain,
GLogLevelFlags level,
va_list args);
void bolt_log (const char *domain,
GLogLevelFlags level,
...);
/* consumer functions */
const char * bolt_log_level_to_priority (GLogLevelFlags log_level);
const char * bolt_log_level_to_string (GLogLevelFlags log_level);
typedef struct _BoltLogCtx BoltLogCtx;
BoltLogCtx * bolt_log_ctx_acquire (const GLogField *fields,
gsize n);
gboolean bolt_log_ctx_set_id (BoltLogCtx *ctx,
const char *id);
void bolt_log_ctx_free (BoltLogCtx *ctx);
const char * blot_log_ctx_get_domain (BoltLogCtx *ctx);
void bolt_log_fmt_journal (const BoltLogCtx *ctx,
GLogLevelFlags log_level,
char *message,
gsize size);
GLogWriterOutput bolt_log_stdstream (const BoltLogCtx *ctx,
GLogLevelFlags log_level,
guint flags);
GLogWriterOutput bolt_log_journal (const BoltLogCtx *ctx,
GLogLevelFlags log_level,
guint flags);
void bolt_log_gen_id (char id[BOLT_LOG_MSG_IDLEN]);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (BoltLogCtx, bolt_log_ctx_free);
G_END_DECLS
bolt-0.9.2/boltd/bolt-manager.c 0000664 0000000 0000000 00000235053 14174530510 0016312 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-bouncer.h"
#include "bolt-config.h"
#include "bolt-device.h"
#include "bolt-domain.h"
#include "bolt-error.h"
#include "bolt-log.h"
#include "bolt-power.h"
#include "bolt-store.h"
#include "bolt-str.h"
#include "bolt-sysfs.h"
#include "bolt-time.h"
#include "bolt-udev.h"
#include "bolt-unix.h"
#include "bolt-watchdog.h"
#include "bolt-manager.h"
#include
#include
#define PROBING_SETTLE_TIME_MS 2000 /* in milli-seconds */
typedef struct udev_device udev_device;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (udev_device, udev_device_unref);
static void bolt_manager_initable_iface_init (GInitableIface *iface);
static gboolean bolt_manager_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error);
static gboolean bolt_manager_store_init (BoltManager *mgr,
GError **error);
static void bolt_manager_store_upgrade (BoltManager *mgr,
gboolean *upgraded);
/* internal manager functions */
static void manager_sd_notify_status (BoltManager *mgr);
/* domain related functions */
static gboolean manager_load_domains (BoltManager *mgr,
GError **error);
static void manager_bootacl_inital_sync (BoltManager *mgr,
BoltDomain *domain);
static BoltDomain * manager_domain_ensure (BoltManager *mgr,
struct udev_device *dev);
static BoltDomain * manager_find_domain_by_syspath (BoltManager *mgr,
const char *syspath);
static void manager_register_domain (BoltManager *mgr,
BoltDomain *domain);
static void manager_deregister_domain (BoltManager *mgr,
BoltDomain *domain);
static void manager_cleanup_stale_domains (BoltManager *mgr);
/* device related functions */
static gboolean manager_load_devices (BoltManager *mgr,
GError **error);
static void manager_register_device (BoltManager *mgr,
BoltDevice *device);
static void manager_deregister_device (BoltManager *mgr,
BoltDevice *device);
static BoltDevice * manager_find_device_by_syspath (BoltManager *mgr,
const char *sysfs);
static BoltDevice * manager_find_device_by_uid (BoltManager *mgr,
const char *uid,
GError **error);
static BoltDevice * bolt_manager_get_parent (BoltManager *mgr,
BoltDevice *dev);
static GPtrArray * bolt_manager_get_children (BoltManager *mgr,
BoltDevice *target);
static void bolt_manager_label_device (BoltManager *mgr,
BoltDevice *target);
/* udev events */
static void handle_uevent_udev (BoltUdev *udev,
const char *action,
struct udev_device *device,
gpointer user_data);
static void handle_udev_domain_event (BoltManager *mgr,
struct udev_device *device,
const char *action);
static void handle_udev_domain_removed (BoltManager *mgr,
BoltDomain *domain);
static void handle_udev_device_event (BoltManager *mgr,
struct udev_device *device,
const char *action);
static void handle_udev_device_added (BoltManager *mgr,
BoltDomain *domain,
struct udev_device *udev);
static void handle_udev_device_changed (BoltManager *mgr,
BoltDevice *dev,
struct udev_device *udev);
static void handle_udev_device_removed (BoltManager *mgr,
BoltDevice *dev);
static void handle_udev_device_attached (BoltManager *mgr,
BoltDomain *domain,
BoltDevice *dev,
struct udev_device *udev);
static void handle_udev_device_detached (BoltManager *mgr,
BoltDevice *dev);
/* signal callbacks */
static void handle_store_device_added (BoltStore *store,
const char *uid,
BoltManager *mgr);
static void handle_store_device_removed (BoltStore *store,
const char *uid,
BoltManager *mgr);
static void handle_domain_security_changed (BoltManager *mgr,
GParamSpec *unused,
BoltDomain *domain);
static void handle_device_status_changed (BoltDevice *dev,
BoltStatus old,
BoltManager *mgr);
static void handle_device_generation_changed (BoltDevice *dev,
GParamSpec *unused,
BoltManager *mgr);
static void handle_power_state_changed (GObject *gobject,
GParamSpec *pspec,
gpointer user_data);
/* acquiring indicator */
static void manager_probing_device_added (BoltManager *mgr,
struct udev_device *dev);
static void manager_probing_device_removed (BoltManager *mgr,
struct udev_device *dev);
static void manager_probing_domain_added (BoltManager *mgr,
struct udev_device *domain);
static void manager_probing_activity (BoltManager *mgr,
gboolean weak);
/* force powering */
static BoltGuard * manager_maybe_power_controller (BoltManager *mgr);
/* config */
static void manager_load_user_config (BoltManager *mgr);
/* dbus property setter */
static gboolean handle_set_authmode (BoltExported *obj,
const char *name,
const GValue *value,
GError **error);
/* dbus method calls */
static GVariant * handle_list_domains (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error);
static GVariant * handle_domain_by_id (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error);
static GVariant * handle_list_devices (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error);
static GVariant * handle_device_by_uid (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error);
static GVariant * handle_enroll_device (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error);
static GVariant * handle_forget_device (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error);
/* */
struct _BoltManager
{
BoltExported object;
/* udev */
BoltUdev *udev;
/* state */
BoltStore *store;
BoltDomain *domains;
GPtrArray *devices;
BoltPower *power;
BoltSecurity security;
BoltAuthMode authmode;
guint generation;
/* policy enforcer */
BoltBouncer *bouncer;
/* config */
GKeyFile *config;
BoltPolicy policy; /* default enrollment policy, unless specified */
/* probing indicator */
guint authorizing; /* number of devices currently authorizing */
GPtrArray *probing_roots; /* pci device tree root */
guint probing_timeout; /* signal id & indicator */
gint64 probing_tstamp; /* time stamp of last activity */
guint probing_tsettle; /* how long to indicate after the last activity */
/* watchdog */
BoltWatchdog *dog;
};
enum {
PROP_0,
PROP_VERSION,
PROP_PROBING,
PROP_POLICY,
PROP_SECURITY,
PROP_AUTHMODE,
PROP_POWERSTATE,
PROP_GENERATION,
PROP_LAST,
PROP_EXPORTED = PROP_VERSION
};
static GParamSpec *props[PROP_LAST] = {NULL, };
G_DEFINE_TYPE_WITH_CODE (BoltManager,
bolt_manager,
BOLT_TYPE_EXPORTED,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
bolt_manager_initable_iface_init));
static void
bolt_manager_finalize (GObject *object)
{
BoltManager *mgr = BOLT_MANAGER (object);
g_clear_object (&mgr->udev);
if (mgr->probing_timeout)
{
g_source_remove (mgr->probing_timeout);
mgr->probing_timeout = 0;
}
g_clear_pointer (&mgr->probing_roots, g_ptr_array_unref);
g_clear_object (&mgr->store);
g_ptr_array_free (mgr->devices, TRUE);
bolt_domain_clear (&mgr->domains);
g_clear_pointer (&mgr->config, g_key_file_unref);
g_clear_object (&mgr->power);
g_clear_object (&mgr->bouncer);
g_clear_object (&mgr->dog);
G_OBJECT_CLASS (bolt_manager_parent_class)->finalize (object);
}
static void
bolt_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltManager *mgr = BOLT_MANAGER (object);
switch (prop_id)
{
case PROP_VERSION:
g_value_set_uint (value, BOLT_DBUS_API_VERSION);
break;
case PROP_PROBING:
g_value_set_boolean (value, mgr->probing_timeout > 0);
break;
case PROP_POLICY:
g_value_set_enum (value, mgr->policy);
break;
case PROP_SECURITY:
g_value_set_enum (value, mgr->security);
break;
case PROP_AUTHMODE:
g_value_set_flags (value, mgr->authmode);
break;
case PROP_POWERSTATE:
g_value_set_enum (value, bolt_power_get_state (mgr->power));
break;
case PROP_GENERATION:
g_value_set_uint (value, mgr->generation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_manager_init (BoltManager *mgr)
{
mgr->devices = g_ptr_array_new_with_free_func (g_object_unref);
mgr->probing_roots = g_ptr_array_new_with_free_func (g_free);
mgr->probing_tsettle = PROBING_SETTLE_TIME_MS; /* milliseconds */
mgr->security = BOLT_SECURITY_UNKNOWN;
/* default configuration */
mgr->policy = BOLT_POLICY_AUTO;
mgr->authmode = BOLT_AUTH_ENABLED;
}
static void
bolt_manager_class_init (BoltManagerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
BoltExportedClass *exported_class = BOLT_EXPORTED_CLASS (klass);
gobject_class->finalize = bolt_manager_finalize;
gobject_class->get_property = bolt_manager_get_property;
props[PROP_VERSION] =
g_param_spec_uint ("version", "Version", "Version",
0, G_MAXUINT32, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_PROBING] =
g_param_spec_boolean ("probing", "Probing", "Probing",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_POLICY] =
g_param_spec_enum ("default-policy", "DefaultPolicy", "DefaultPolicy",
BOLT_TYPE_POLICY,
BOLT_POLICY_AUTO,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_SECURITY] =
g_param_spec_enum ("security-level", "SecurityLevel", NULL,
BOLT_TYPE_SECURITY,
BOLT_SECURITY_UNKNOWN,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_AUTHMODE] =
g_param_spec_flags ("auth-mode", "AuthMode", NULL,
BOLT_TYPE_AUTH_MODE,
BOLT_AUTH_ENABLED,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_POWERSTATE] =
g_param_spec_enum ("power-state", "PowerState", NULL,
BOLT_TYPE_POWER_STATE,
BOLT_FORCE_POWER_UNSET,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_GENERATION] =
g_param_spec_uint ("generation", "Generation", NULL,
0, G_MAXUINT, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, props);
bolt_exported_class_set_interface_info (exported_class,
BOLT_DBUS_INTERFACE,
BOLT_DBUS_GRESOURCE_PATH);
bolt_exported_class_export_properties (exported_class,
PROP_EXPORTED,
PROP_LAST,
props);
bolt_exported_class_property_setter (exported_class,
props[PROP_AUTHMODE],
handle_set_authmode);
bolt_exported_class_export_method (exported_class,
"ListDomains",
handle_list_domains);
bolt_exported_class_export_method (exported_class,
"DomainById",
handle_domain_by_id);
bolt_exported_class_export_method (exported_class,
"ListDevices",
handle_list_devices);
bolt_exported_class_export_method (exported_class,
"DeviceByUid",
handle_device_by_uid);
bolt_exported_class_export_method (exported_class,
"EnrollDevice",
handle_enroll_device);
bolt_exported_class_export_method (exported_class,
"ForgetDevice",
handle_forget_device);
}
static void
bolt_manager_initable_iface_init (GInitableIface *iface)
{
iface->init = bolt_manager_initialize;
}
static gboolean
bolt_manager_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
g_autoptr(BoltGuard) power = NULL;
BoltManager *mgr;
struct udev_enumerate *enumerate;
struct udev_list_entry *l, *devices;
gboolean upgraded = FALSE;
gboolean ok;
mgr = BOLT_MANAGER (initable);
/* store setup */
ok = bolt_manager_store_init (mgr, error);
if (!ok)
return FALSE;
/* load dynamic user configuration */
manager_load_user_config (mgr);
/* polkit setup */
mgr->bouncer = bolt_bouncer_new (cancellable, error);
if (mgr->bouncer == NULL)
return FALSE;
bolt_bouncer_add_client (mgr->bouncer, mgr);
/* watchdog setup */
mgr->dog = bolt_watchdog_new (error);
if (mgr->dog == NULL)
return FALSE;
/* udev setup*/
bolt_info (LOG_TOPIC ("udev"), "initializing udev");
mgr->udev = bolt_udev_new ("udev", NULL, error);
if (mgr->udev == NULL)
return FALSE;
g_signal_connect_object (mgr->udev, "uevent",
(GCallback) handle_uevent_udev,
mgr, 0);
ok = manager_load_domains (mgr, error);
if (!ok)
return FALSE;
ok = manager_load_devices (mgr, error);
if (!ok)
return FALSE;
/* setup the power controller */
mgr->power = bolt_power_new (mgr->udev);
bolt_bouncer_add_client (mgr->bouncer, mgr->power);
g_signal_connect_object (mgr->power, "notify::state",
G_CALLBACK (handle_power_state_changed),
mgr, 0);
/* if we don't see any tb device, we try to force power */
power = manager_maybe_power_controller (mgr);
if (power != NULL)
bolt_info (LOG_TOPIC ("manager"), "acquired power guard '%s'",
bolt_guard_get_id (power));
/* TODO: error checking */
enumerate = bolt_udev_new_enumerate (mgr->udev, NULL);
udev_enumerate_add_match_subsystem (enumerate, "thunderbolt");
/* only devices (i.e. not the domain controller) */
bolt_info (LOG_TOPIC ("udev"), "enumerating devices");
udev_enumerate_scan_devices (enumerate);
devices = udev_enumerate_get_list_entry (enumerate);
udev_list_entry_foreach (l, devices)
{
g_autoptr(GError) err = NULL;
g_autoptr(udev_device) udevice = NULL;
const char *syspath;
const char *devtype;
syspath = udev_list_entry_get_name (l);
udevice = bolt_udev_device_new_from_syspath (mgr->udev,
syspath,
&err);
if (udevice == NULL)
{
bolt_warn_err (err, "enumerating devices");
continue;
}
devtype = udev_device_get_devtype (udevice);
if (bolt_streq (devtype, "thunderbolt_domain"))
handle_udev_domain_event (mgr, udevice, "add");
if (bolt_streq (devtype, "thunderbolt_device"))
handle_udev_device_event (mgr, udevice, "add");
}
udev_enumerate_unref (enumerate);
/* upgrade the store, if needed */
bolt_manager_store_upgrade (mgr, &upgraded);
if (upgraded)
manager_cleanup_stale_domains (mgr);
manager_sd_notify_status (mgr);
return TRUE;
}
static gboolean
bolt_manager_store_init (BoltManager *mgr, GError **error)
{
bolt_info (LOG_TOPIC ("manager"), "initializing store");
mgr->store = bolt_store_new (bolt_get_store_path (), error);
if (mgr->store == NULL)
return FALSE;
g_signal_connect_object (mgr->store, "device-added",
G_CALLBACK (handle_store_device_added),
mgr, 0);
g_signal_connect_object (mgr->store, "device-removed",
G_CALLBACK (handle_store_device_removed),
mgr, 0);
return TRUE;
}
static void
bolt_manager_store_upgrade (BoltManager *mgr, gboolean *upgraded)
{
g_autoptr(GError) err = NULL;
BoltStore *store = mgr->store;
guint ver;
gboolean ok;
ver = bolt_store_get_version (store);
if (ver == BOLT_STORE_VERSION)
{
bolt_debug (LOG_TOPIC ("store"), "store is up to date");
return;
}
bolt_info (LOG_TOPIC ("store"), "attempting upgrade from '%d'",
ver);
ok = bolt_store_upgrade (store, NULL, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("store"), "upgrade failed");
return;
}
ver = bolt_store_get_version (store);
bolt_info (LOG_TOPIC ("store"), "upgraded to version '%d'",
ver);
*upgraded = TRUE;
}
/* internal functions */
static void
manager_sd_notify_status (BoltManager *mgr)
{
g_autoptr(GError) err = NULL;
g_autofree char *status = NULL;
const char *pstate = NULL;
BoltPowerState power;
gboolean sent;
gboolean authorizing;
gboolean ok = FALSE;
authorizing = bolt_flag_isset (mgr->authmode, BOLT_AUTH_ENABLED);
power = bolt_power_get_state (mgr->power);
pstate = bolt_enum_to_string (BOLT_TYPE_POWER_STATE, power, NULL);
status = g_strdup_printf ("STATUS=authmode: %s,"
" force-power: %s",
(authorizing ? "enabled" : "DISABLED"),
(pstate ? : "unknown"));
ok = bolt_sd_notify_literal (status, &sent, &err);
if (!ok)
bolt_warn_err (err, LOG_TOPIC ("status"),
"failed to send status");
else
bolt_debug (LOG_TOPIC ("status"), "%s [sent: %s]",
status, bolt_yesno (sent));
}
static void
manager_maybe_set_security (BoltManager *mgr,
BoltSecurity security)
{
/* update the security level, if it is not already
* set, but also ignore if security is 'unknown' */
if (security == BOLT_SECURITY_UNKNOWN)
return;
if (mgr->security == BOLT_SECURITY_UNKNOWN)
{
bolt_info ("security level set to '%s'",
bolt_security_to_string (security));
mgr->security = security;
g_object_notify_by_pspec (G_OBJECT (mgr),
props[PROP_SECURITY]);
}
else if (mgr->security != security)
{
bolt_warn ("multiple security levels (%s vs %s)",
bolt_security_to_string (mgr->security),
bolt_security_to_string (security));
}
}
static void
manager_maybe_set_generation (BoltManager *mgr,
guint gen)
{
guint check;
check = MAX (mgr->generation, gen);
if (mgr->generation >= check)
return;
bolt_info ("global 'generation' set to '%d'", check);
mgr->generation = check;
g_object_notify_by_pspec (G_OBJECT (mgr),
props[PROP_GENERATION]);
}
/* domain related function */
static gboolean
manager_load_domains (BoltManager *mgr,
GError **error)
{
g_auto(GStrv) ids = NULL;
ids = bolt_store_list_uids (mgr->store, "domains", error);
if (ids == NULL)
{
g_prefix_error (error, "failed to list domains in store: ");
return FALSE;
}
bolt_info (LOG_TOPIC ("store"), "loading domains");
for (guint i = 0; i < g_strv_length (ids); i++)
{
g_autoptr(GError) err = NULL;
BoltDomain *dom = NULL;
const char *uid = ids[i];
bolt_info (LOG_TOPIC ("store"), LOG_DOM_UID (uid),
"loading domain");
dom = bolt_store_get_domain (mgr->store, uid, &err);
if (dom == NULL)
{
bolt_warn_err (err, LOG_DOM_UID (uid), LOG_TOPIC ("store"),
"failed to load domain", uid);
continue;
}
manager_register_domain (mgr, dom);
}
return TRUE;
}
static void
manager_bootacl_inital_sync (BoltManager *mgr,
BoltDomain *domain)
{
g_autoptr(GError) err = NULL;
g_auto(GStrv) acl = NULL;
gboolean ok;
guint n, empty;
if (!bolt_domain_supports_bootacl (domain))
{
bolt_info (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"bootacl not supported, no sync");
return;
}
acl = bolt_domain_dup_bootacl (domain);
g_assert (acl != NULL);
n = bolt_domain_bootacl_slots (domain, &empty);
bolt_info (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"sync start [slots: %u free: %u]", n, empty);
for (guint i = 0; i < mgr->devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (mgr->devices, i);
const char *duid = bolt_device_get_uid (dev);
gboolean polok, inacl, sync;
polok = bolt_device_get_policy (dev) == BOLT_POLICY_AUTO;
inacl = bolt_domain_bootacl_contains (domain, duid);
sync = polok && !inacl;
bolt_info (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
LOG_DEV_UID (duid),
"sync '%.13s…' %s [policy: %3s, in acl: %3s]",
duid, bolt_yesno (sync), bolt_yesno (polok),
bolt_yesno (inacl));
if (!sync)
continue;
bolt_domain_bootacl_allocate (domain, acl, duid);
}
ok = bolt_domain_bootacl_set (domain, acl, &err);
if (!ok && err != NULL)
{
bolt_warn_err (err, LOG_DOM (domain), "failed to write bootacl");
return;
}
bolt_domain_bootacl_slots (domain, &empty);
bolt_info (LOG_TOPIC ("bootacl"), LOG_DOM (domain),
"sync done [wrote: %s, now free: %u]",
bolt_yesno (ok), empty);
}
static gboolean
domain_has_stable_uuid (BoltDomain *domain,
struct udev_device *dev)
{
g_autoptr(GError) err = NULL;
gboolean stable;
gboolean ok;
guint32 pci_id;
/* On integrated TBT, like ICL/TGL, the uuid of the
* controller is randomly generated on *every* boot,
* and thus the uuid is not stable. */
/* default to FALSE, in case we have no entry */
stable = FALSE;
ok = bolt_sysfs_nhi_id_for_domain (dev, &pci_id, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("udev"), LOG_DOM (domain),
"failed to get NHI for domain");
return FALSE;
}
ok = bolt_nhi_uuid_is_stable (pci_id, &stable, &err);
if (!ok)
bolt_warn_err (err, LOG_TOPIC ("udev"), LOG_DOM (domain),
"failed to determine if uid is stable");
bolt_info (LOG_TOPIC ("udev"), LOG_DOM (domain),
"uuid is stable: %s (for NHI: 0x%04x)",
bolt_yesno (stable), pci_id);
return stable;
}
static void
manager_store_domain (BoltManager *mgr,
BoltDomain *domain)
{
g_autoptr(GError) err = NULL;
gboolean ok;
bolt_info (LOG_TOPIC ("store"), LOG_DOM (domain),
"storing newly connected domain");
ok = bolt_store_put_domain (mgr->store, domain, &err);
if (!ok)
bolt_warn_err (err, LOG_TOPIC ("store"), LOG_DOM (domain),
"could not store domain");
}
static BoltDomain *
manager_domain_ensure (BoltManager *mgr,
struct udev_device *dev)
{
g_autoptr(GError) err = NULL;
BoltSecurity level;
BoltDomain *domain = NULL;
GDBusConnection *bus;
struct udev_device *host;
struct udev_device *dom;
const char *security;
const char *syspath;
const char *op;
const char *uid;
gboolean iommu;
/* check if we already know a domain that is the parent
* of the device (dev); if not then 'dev' is very likely
* the host device.
*/
syspath = udev_device_get_syspath (dev);
domain = manager_find_domain_by_syspath (mgr, syspath);
if (domain != NULL)
return domain;
/* dev is very likely the host, i.e. root switch device,
* but we might as well make sure we can get the domain
* from any; also we make sure that we have indeed the
* host device via this lookup.
*/
dom = bolt_sysfs_domain_for_device (dev, &host);
if (dom == NULL)
return NULL;
uid = bolt_sysfs_device_get_unique_id (host, NULL);
/* check if we have a stored domain with a matching uid
* of the host device, if so then we have just connected
* the corresponding domain controller, represented by
* the 'dom' udev_device.
*/
domain = bolt_domain_find_id (mgr->domains, uid, NULL);
if (domain != NULL)
{
bolt_domain_connected (domain, dom);
return domain;
}
/* this is an unknown, unstored domain controller */
domain = bolt_domain_new_for_udev (dom, uid, &err);
if (domain == NULL)
{
bolt_warn_err (err, LOG_TOPIC ("udev"),
"failed to create domain: %s");
return NULL;
}
level = bolt_domain_get_security (domain);
iommu = bolt_domain_has_iommu (domain);
security = bolt_security_for_display (level, iommu);
bolt_msg (LOG_DOM (domain), "newly connected [%s] (%s)",
security, syspath);
manager_maybe_set_security (mgr, level);
manager_register_domain (mgr, domain);
/* registering the domain will add one reference */
g_object_unref (domain);
/* add all devices with POLICY_AUTO to the bootacl */
manager_bootacl_inital_sync (mgr, domain);
/* now store the domain (with an updated bootacl),
* but only if its uuid is the same across reboots */
if (domain_has_stable_uuid (domain, dom))
manager_store_domain (mgr, domain);
/* export it on the bus and emit the added signals */
bus = bolt_exported_get_connection (BOLT_EXPORTED (mgr));
if (bus == NULL)
return domain;
bolt_domain_export (domain, bus);
op = bolt_exported_get_object_path (BOLT_EXPORTED (domain));
bolt_exported_emit_signal (BOLT_EXPORTED (mgr),
"DomainAdded",
g_variant_new ("(o)", op),
NULL);
return domain;
}
static BoltDomain *
manager_find_domain_by_syspath (BoltManager *mgr,
const char *syspath)
{
BoltDomain *iter = mgr->domains;
guint n_domains;
n_domains = bolt_domain_count (mgr->domains);
for (guint i = 0; i < n_domains; i++)
{
const char *prefix = bolt_domain_get_syspath (iter);
/* we get a perfect match, if we search for the domain
* itself, or if we are looking for the domain that
* is the parent of the device in @syspath */
if (prefix && g_str_has_prefix (syspath, prefix))
return iter;
iter = bolt_domain_next (iter);
}
return NULL;
}
static gboolean
bootacl_alloc (BoltDomain *domain,
GStrv acl,
const char *uid,
gint *slot,
BoltManager *mgr)
{
char **target = NULL;
guint64 last = G_MAXUINT64;
/* if we have a valid allocation, just use that */
if (*slot != -1)
return TRUE;
g_return_val_if_fail (acl != NULL, FALSE);
/* we need to replace one of devices currently in the acl */
for (char **iter = acl; *iter; iter++)
{
g_autoptr(BoltDevice) dev = NULL;
guint64 ts;
dev = manager_find_device_by_uid (mgr, uid, NULL);
/* device might not be managed by us */
if (dev == NULL)
continue;
/* authtime is the last time a device was seen and
* successfully authorized; choose the one that
* has the *oldest* timestamp (smallest number) */
ts = bolt_device_get_authtime (dev);
if (ts < last)
{
target = iter;
last = ts;
}
}
if (target == NULL)
return FALSE;
*slot = target - acl;
return TRUE;
}
static void
manager_register_domain (BoltManager *mgr,
BoltDomain *domain)
{
guint n_slots, n_free;
mgr->domains = bolt_domain_insert (mgr->domains, domain);
n_slots = bolt_domain_bootacl_slots (domain, &n_free);
bolt_info (LOG_TOPIC ("domain"), LOG_DOM (domain),
"registered (bootacl: %u/%u)",
n_free, n_slots);
g_signal_connect_object (domain, "bootacl-alloc",
G_CALLBACK (bootacl_alloc), mgr, 0);
g_signal_connect_object (domain, "notify::security",
G_CALLBACK (handle_domain_security_changed),
mgr, G_CONNECT_SWAPPED);
bolt_bouncer_add_client (mgr->bouncer, domain);
}
static void
manager_deregister_domain (BoltManager *mgr,
BoltDomain *domain)
{
bolt_info (LOG_TOPIC ("manager"), LOG_DOM (domain),
"de-registered");
mgr->domains = bolt_domain_remove (mgr->domains, domain);
}
static void
manager_cleanup_stale_domains (BoltManager *mgr)
{
g_autoptr(GPtrArray) domains = NULL;
BoltDomain *iter;
guint count;
/* Before bolt 0.9.1, domains were stored that have an
* unstable uuid, i.e. their uuids change on every boot
* and thus there will be store entries that can't be
* matched anymore and thus are "stale".
*/
bolt_info (LOG_TOPIC ("manager"), "stale domain cleanup");
count = bolt_domain_count (mgr->domains);
domains = g_ptr_array_new_full (count, g_object_unref);
iter = mgr->domains;
for (guint i = 0; i < count; i++)
{
gboolean stored = bolt_domain_is_stored (iter);
gboolean offline = !bolt_domain_is_connected (iter);
if (stored && offline)
g_ptr_array_add (domains, g_object_ref (iter));
iter = bolt_domain_next (iter);
}
for (guint i = 0; i < domains->len; i++)
{
g_autoptr(GError) err = NULL;
BoltDomain *dom = g_ptr_array_index (domains, i);
gboolean ok;
bolt_info (LOG_DOM (dom), LOG_TOPIC ("store"),
"stale domain detected");
ok = bolt_store_del_domain (mgr->store, dom, &err);
if (!ok)
{
bolt_warn_err (err, LOG_DOM (dom),
"failed to delete domain");
continue;
}
manager_deregister_domain (mgr, dom);
}
}
/* device related functions */
static gboolean
manager_load_devices (BoltManager *mgr,
GError **error)
{
g_auto(GStrv) ids = NULL;
ids = bolt_store_list_uids (mgr->store, "devices", error);
if (ids == NULL)
{
g_prefix_error (error, "failed to list devices in store: ");
return FALSE;
}
bolt_info (LOG_TOPIC ("store"), "loading devices");
for (guint i = 0; i < g_strv_length (ids); i++)
{
g_autoptr(GError) err = NULL;
BoltDevice *dev = NULL;
const char *uid = ids[i];
bolt_info (LOG_DEV_UID (uid), LOG_TOPIC ("store"), "loading device");
dev = bolt_store_get_device (mgr->store, uid, &err);
if (dev == NULL)
{
bolt_warn_err (err, LOG_TOPIC ("store"),
LOG_DIRECT (BOLT_LOG_DEVICE_UID, uid),
"failed to load device (%.7s)", uid);
continue;
}
manager_register_device (mgr, dev);
}
return TRUE;
}
static void
manager_register_device (BoltManager *mgr,
BoltDevice *dev)
{
g_ptr_array_add (mgr->devices, dev);
bolt_bouncer_add_client (mgr->bouncer, dev);
g_signal_connect_object (dev, "status-changed",
G_CALLBACK (handle_device_status_changed),
mgr, 0);
if (bolt_device_is_host (dev))
{
guint generation = bolt_device_get_generation (dev);
manager_maybe_set_generation (mgr, generation);
g_signal_connect_object (dev, "notify::generation",
G_CALLBACK (handle_device_generation_changed),
mgr, 0);
}
}
static void
manager_deregister_device (BoltManager *mgr,
BoltDevice *dev)
{
const char *opath;
g_ptr_array_remove_fast (mgr->devices, dev);
opath = bolt_device_get_object_path (dev);
if (opath == NULL)
return;
bolt_exported_emit_signal (BOLT_EXPORTED (mgr),
"DeviceRemoved",
g_variant_new ("(o)", opath),
NULL);
bolt_device_unexport (dev);
bolt_info (LOG_DEV (dev), LOG_TOPIC ("dbus"), "unexported");
}
static BoltDevice *
manager_find_device_by_syspath (BoltManager *mgr,
const char *sysfs)
{
g_return_val_if_fail (sysfs != NULL, NULL);
for (guint i = 0; i < mgr->devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (mgr->devices, i);
const char *have = bolt_device_get_syspath (dev);
if (bolt_streq (have, sysfs))
return g_object_ref (dev);
}
return NULL;
}
static BoltDevice *
manager_find_device_by_uid (BoltManager *mgr,
const char *uid,
GError **error)
{
if (uid == NULL || uid[0] == '\0')
{
g_set_error_literal (error, G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"empty device unique_id");
return NULL;
}
for (guint i = 0; i < mgr->devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (mgr->devices, i);
if (bolt_streq (bolt_device_get_uid (dev), uid))
return g_object_ref (dev);
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"device with id '%s' could not be found.",
uid);
return NULL;
}
static BoltDevice *
bolt_manager_get_parent (BoltManager *mgr,
BoltDevice *dev)
{
g_autofree char *path = NULL;
const char *syspath;
const char *start;
char *pos;
syspath = bolt_device_get_syspath (dev);
if (syspath == NULL)
return NULL;
path = g_strdup (syspath);
start = path + strlen ("/sys");
pos = strrchr (start, '/');
if (!pos || pos < start + 2)
return NULL;
*pos = '\0';
return manager_find_device_by_syspath (mgr, path);
}
static GPtrArray *
bolt_manager_get_children (BoltManager *mgr,
BoltDevice *target)
{
GPtrArray *res;
res = g_ptr_array_new_with_free_func (g_object_unref);
for (guint i = 0; i < mgr->devices->len; i++)
{
g_autoptr(BoltDevice) parent = NULL;
BoltDevice *dev = g_ptr_array_index (mgr->devices, i);
parent = bolt_manager_get_parent (mgr, dev);
if (parent != target)
continue;
g_ptr_array_add (res, g_object_ref (dev));
}
return res;
}
static void
bolt_manager_label_device (BoltManager *mgr,
BoltDevice *target)
{
g_autofree char *label = NULL;
const char *name;
const char *vendor;
guint count = 0;
static struct
{
const char *from;
const char *to;
} vendors[] = {
{"Dell Inc.", "Dell" },
{"HP Inc.", "HP" },
{"Apple, Inc.", "Apple"}
};
name = bolt_device_get_name (target);
vendor = bolt_device_get_vendor (target);
/* we count how many duplicate devices we have */
for (guint i = 0; i < mgr->devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (mgr->devices, i);
const char *dev_name = bolt_device_get_name (dev);
const char *dev_vendor = bolt_device_get_vendor (dev);
if (bolt_streq (dev_name, name) &&
bolt_streq (dev_vendor, vendor))
count++;
}
/* cleanup name: nicer display names for vendors */
for (guint i = 0; i < G_N_ELEMENTS (vendors); i++)
if (bolt_streq (vendor, vendors[i].from))
vendor = vendors[i].to;
/* cleanup name: Vendor Vendor Device -> Vendor Device */
if (g_str_has_prefix (name, vendor))
{
name += strlen (vendor);
while (g_ascii_isspace (*name))
name++;
if (*name == '\0')
name = bolt_device_get_name (target);
}
/* we counted the target too, > 1 means duplicates */
if (count > 1 && !bolt_device_is_host (target))
label = g_strdup_printf ("%s %s #%u", vendor, name, count);
else
label = g_strdup_printf ("%s %s", vendor, name);
bolt_info (LOG_DEV (target), "labeling device: %s", label);
g_object_set (G_OBJECT (target), "label", label, NULL);
}
/* device authorization */
static void
auto_auth_done (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) err = NULL;
BoltDevice *dev = BOLT_DEVICE (source);
BoltAuth *auth = BOLT_AUTH (res);
gboolean ok;
ok = bolt_auth_check (auth, &err);
if (!ok)
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("auto-auth"),
"authorization failed");
else
bolt_msg (LOG_DEV (dev), LOG_TOPIC ("auto-auth"),
"authorization successful");
}
static void
manager_auto_authorize (BoltManager *mgr,
BoltDevice *dev)
{
g_autoptr(BoltAuth) auth = NULL;
g_autoptr(BoltKey) key = NULL;
g_autofree char *amstr = NULL;
BoltStatus status;
BoltPolicy policy;
gboolean authmode;
gboolean authorize;
gboolean iommu;
BoltSecurity level;
gboolean ok;
status = bolt_device_get_status (dev);
policy = bolt_device_get_policy (dev);
/* more of a sanity check, because we should only end up
* here if the device is not yet authorized and stored */
if (bolt_status_is_authorized (status) ||
!bolt_device_get_stored (dev))
return;
/* The default is not to authorize anything */
authorize = FALSE;
/* The following sections are security critical and thus
* intentionally verbose and explicit */
/* 1) global auth mode and policy check */
authmode = bolt_auth_mode_is_enabled (mgr->authmode);
amstr = bolt_auth_mode_to_string (authmode);
iommu = bolt_device_has_iommu (dev);
level = bolt_device_get_security (dev);
if (authmode)
{
if (policy == BOLT_POLICY_AUTO)
authorize = TRUE;
else if (policy == BOLT_POLICY_IOMMU && iommu)
authorize = TRUE;
}
bolt_msg (LOG_DEV (dev), LOG_TOPIC ("auto-auth"),
"authmode: %s, policy: %s, iommu: %s -> %s",
amstr, bolt_policy_to_string (policy),
bolt_yesno (iommu), bolt_okfail (authorize));
if (!authorize)
return;
/* 2) security level and key check: if we are in SECURE
* mode but don't have a key, we DON'T authorize */
if (level == BOLT_SECURITY_SECURE)
{
g_autoptr(GError) err = NULL;
ok = bolt_device_load_key (dev, &key, &err);
if (!ok)
bolt_warn_err (err, LOG_DEV (dev), "could not load key");
authorize = (key != NULL);
}
bolt_msg (LOG_DEV (dev), LOG_TOPIC ("auto-auth"),
"security: %s mode, key: %s -> %s",
bolt_security_for_display (level, iommu),
bolt_yesno (key), bolt_okfail (authorize));
if (!authorize)
return;
auth = bolt_auth_new (mgr, level, key);
bolt_device_authorize_idle (dev, auth, auto_auth_done, mgr);
}
static void
manager_do_import_device (BoltManager *mgr,
BoltDevice *dev,
BoltPolicy policy)
{
g_autoptr(GError) err = NULL;
gboolean ok;
ok = bolt_store_put_device (mgr->store,
dev,
policy,
NULL,
&err);
if (!ok)
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("import"),
"failed to store device");
}
static void
manager_maybe_import (BoltManager *mgr,
BoltDevice *dev)
{
BoltSecurity level;
BoltPolicy policy;
const char *secstr;
const char *polstr;
gboolean sl0, sl1;
gboolean boot, pcie;
gboolean import;
gboolean iommu;
/* This function is intentionally more verbose then it
* could be, but since it is security critical, it is
* better to be clear than concise */
g_return_if_fail (!bolt_device_get_stored (dev));
g_return_if_fail (bolt_device_is_authorized (dev));
if (bolt_device_is_host (dev))
{
BoltDomain *dom = bolt_device_get_domain (dev);
/* host devices are per design authorized and
* must therefore never be authorized by bolt */
policy = BOLT_POLICY_MANUAL;
/* Store the host device only if its domain is
* stored as well. Currently, the only reason
* for a domain to not be stored is that its
* uuid is not stable, i.e. changes every boot.
* But its uuid is in fact derived from the
* associated host device, i.e. this very host
* device here. Ergo, its uuid is unstable and
* thus it should not be stored */
if (bolt_domain_is_stored (dom))
manager_do_import_device (mgr, dev, policy);
return;
}
level = bolt_device_get_security (dev);
iommu = bolt_device_has_iommu (dev);
boot = bolt_device_check_authflag (dev, BOLT_AUTH_BOOT);
pcie = bolt_security_allows_pcie (level);
sl0 = level == BOLT_SECURITY_NONE;
sl1 = level == BOLT_SECURITY_USER;
/* Check if we want to import that device at all,
* the fundamental rule is: if it was authorized
* by the firmware and we have pcie tunnels we want
* to import it so we have a record of it */
import = pcie && (boot || sl0);
if (import && !iommu && sl1)
policy = BOLT_POLICY_AUTO;
else
policy = BOLT_POLICY_IOMMU;
secstr = bolt_security_for_display (level, iommu);
polstr = bolt_policy_to_string (policy);
bolt_msg (LOG_DEV (dev), LOG_TOPIC ("import"),
"%s mode, boot: %s -> %s",
secstr, bolt_yesno (boot),
(import ? polstr : "no import"));
if (import)
manager_do_import_device (mgr, dev, policy);
}
static void
auto_enroll_done (GObject *device,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) err = NULL;
BoltDevice *dev = BOLT_DEVICE (device);
BoltAuth *auth = BOLT_AUTH (res);
BoltManager *mgr;
BoltPolicy policy;
BoltKey *key;
gboolean ok;
mgr = BOLT_MANAGER (bolt_auth_get_origin (auth));
ok = bolt_auth_check (auth, &err);
if (!ok)
{
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("auto-enroll"),
"failed to authorize the new device");
return;
}
key = bolt_auth_get_key (auth);
policy = bolt_auth_get_policy (auth);
ok = bolt_store_put_device (mgr->store, dev, policy, key, &err);
if (ok)
bolt_msg (LOG_DEV (dev), LOG_TOPIC ("auto-enroll"), "done");
else
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("auto-enroll"),
"failed to store the device");
}
static BoltAuth *
manager_enroll_device_prepare (BoltManager *mgr,
BoltDevice *dev,
GError **error)
{
g_autoptr(BoltDevice) parent = NULL;
g_autoptr(BoltKey) key = NULL;
BoltSecurity level;
BoltAuth *auth;
/* are we even allowed to enroll new devices right now? */
if (bolt_auth_mode_is_disabled (mgr->authmode))
{
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
"authorization of new devices is disabled");
return NULL;
}
/* if the parent is not authorized and we try to authorize,
* we will fail and log a warning, so lets check, fail early
* and avoid the warning */
parent = bolt_manager_get_parent (mgr, dev);
if (!bolt_device_is_authorized (parent))
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_AUTHCHAIN,
"parent not authorized, deferring");
return NULL;
}
/* determine the maximum security level we can use, i.e.
* minimum level that the device *and* host support */
if (bolt_device_supports_secure_mode (dev))
level = bolt_device_get_security (dev);
else
level = BOLT_SECURITY_USER;
/* yay, we are in SECURE mode, we need to create a key */
if (level == BOLT_SECURITY_SECURE)
{
key = bolt_key_new (error);
if (key == NULL)
return NULL;
}
auth = bolt_auth_new (mgr, level, key);
/* bolt_auth_new is a wrapper around g_object_new and
* therefore cannot realistically fail. This assertion
* convinces clang's static analysis of that fact. */
g_assert (auth != NULL);
return auth;
}
static void
manager_auto_enroll (BoltManager *mgr,
BoltDevice *dev)
{
g_autoptr(BoltAuth) auth = NULL;
g_autoptr(GError) err = NULL;
BoltStatus status;
gboolean have_key;
g_return_if_fail (!bolt_device_get_stored (dev));
/* sanity check for the state */
status = bolt_device_get_status (dev);
if (!bolt_status_is_pending (status))
return;
/* sanity check for iommu */
if (!bolt_device_has_iommu (dev))
return;
auth = manager_enroll_device_prepare (mgr, dev, &err);
if (!auth)
{
bolt_msg (LOG_DEV (dev), LOG_TOPIC ("auto-enroll"),
"no, pre-check failed: %s", err->message);
return;
}
have_key = bolt_auth_has_key (auth);
bolt_msg (LOG_DEV (dev), LOG_TOPIC ("auto-enroll"),
"yes, key: %s", bolt_yesno (have_key));
bolt_auth_set_policy (auth, BOLT_POLICY_IOMMU);
bolt_device_authorize (dev, auth, auto_enroll_done, mgr);
}
/* udev callbacks */
static void
handle_uevent_udev (BoltUdev *udev,
const char *action,
struct udev_device *device,
gpointer user_data)
{
BoltManager *mgr;
const char *subsystem;
const char *devtype;
const char *syspath;
mgr = BOLT_MANAGER (user_data);
devtype = udev_device_get_devtype (device);
subsystem = udev_device_get_subsystem (device);
syspath = udev_device_get_syspath (device);
if (g_str_equal (action, "add"))
manager_probing_device_added (mgr, device);
else if (g_str_equal (action, "remove"))
manager_probing_device_removed (mgr, device);
/* beyond this point only udev device from the
* thunderbolt are handled */
if (!bolt_streq (subsystem, "thunderbolt"))
return;
bolt_debug (LOG_TOPIC ("udev"), "%s (%s%s%s) %s", action,
subsystem, devtype ? "/" : "", devtype ? : "",
syspath);
if (bolt_streq (devtype, "thunderbolt_device"))
handle_udev_device_event (mgr, device, action);
else if (bolt_streq (devtype, "thunderbolt_domain"))
handle_udev_domain_event (mgr, device, action);
}
static void
handle_udev_domain_event (BoltManager *mgr,
struct udev_device *device,
const char *action)
{
const char *syspath;
BoltDomain *domain;
syspath = udev_device_get_syspath (device);
if (g_str_equal (action, "add"))
{
manager_probing_domain_added (mgr, device);
/* the creation of the actual domain object and
* its registration is handled on-demand: only
* when the host device appears, the uevent
* handler for the thunderbolt devices will
* ensure the domain exists
*/
}
else if (g_str_equal (action, "change"))
{
domain = manager_find_domain_by_syspath (mgr, syspath);
if (domain == NULL)
{
bolt_warn (LOG_TOPIC ("domain"),
"unregistered domain changed at %s",
syspath);
return;
}
bolt_domain_update_from_udev (domain, device);
}
else if (g_str_equal (action, "remove"))
{
domain = manager_find_domain_by_syspath (mgr, syspath);
if (!domain)
{
bolt_warn (LOG_TOPIC ("domain"),
"unregistered domain removed at %s",
syspath);
return;
}
if (bolt_domain_is_stored (domain))
bolt_domain_disconnected (domain);
else
handle_udev_domain_removed (mgr, domain);
}
}
static void
handle_udev_domain_removed (BoltManager *mgr,
BoltDomain *domain)
{
const char *name;
name = bolt_domain_get_id (domain);
bolt_info (LOG_TOPIC ("domain"), "'%s' removed", name);
if (bolt_exported_is_exported (BOLT_EXPORTED (domain)))
{
const char *op;
gboolean ok;
op = bolt_exported_get_object_path (BOLT_EXPORTED (domain));
bolt_exported_emit_signal (BOLT_EXPORTED (mgr),
"DomainRemoved",
g_variant_new ("(o)", op),
NULL);
ok = bolt_exported_unexport (BOLT_EXPORTED (domain));
bolt_info (LOG_TOPIC ("dbus"), "%s unexported: %s",
name, bolt_okfail (ok));
}
manager_deregister_domain (mgr, domain);
}
static void
handle_udev_device_event (BoltManager *mgr,
struct udev_device *device,
const char *action)
{
g_autoptr(BoltDevice) dev = NULL;
const char *syspath;
syspath = udev_device_get_syspath (device);
if (g_str_equal (action, "add") ||
g_str_equal (action, "change"))
{
BoltDomain *dom;
const char *uid;
/* filter sysfs devices (e.g. the domain) that don't have
* the unique_id attribute */
uid = bolt_sysfs_device_get_unique_id (device, NULL);
if (uid == NULL)
return;
dom = manager_domain_ensure (mgr, device);
if (dom == NULL)
{
bolt_warn (LOG_TOPIC ("domain"),
"could not find domain for device at '%s'",
syspath);
return;
}
dev = manager_find_device_by_uid (mgr, uid, NULL);
if (!dev)
handle_udev_device_added (mgr, dom, device);
else if (!bolt_device_is_connected (dev))
handle_udev_device_attached (mgr, dom, dev, device);
else
handle_udev_device_changed (mgr, dev, device);
}
else if (g_str_equal (action, "remove"))
{
const char *name;
/* filter out the domain controller */
name = udev_device_get_sysname (device);
if (name && g_str_has_prefix (name, "domain"))
return;
dev = manager_find_device_by_syspath (mgr, syspath);
/* if we don't have any records of the device,
* then we don't care */
if (!dev)
return;
if (bolt_device_get_stored (dev))
handle_udev_device_detached (mgr, dev);
else
handle_udev_device_removed (mgr, dev);
}
}
static void
handle_udev_device_added (BoltManager *mgr,
BoltDomain *domain,
struct udev_device *udev)
{
g_autoptr(GError) err = NULL;
GDBusConnection *bus;
BoltDevice *dev;
BoltStatus status;
const char *opath;
const char *syspath;
syspath = udev_device_get_syspath (udev);
dev = bolt_device_new_for_udev (udev, domain, &err);
if (dev == NULL)
{
bolt_warn_err (err, LOG_TOPIC ("udev"),
"could not create device for %s",
syspath);
return;
}
manager_register_device (mgr, dev);
status = bolt_device_get_status (dev);
bolt_msg (LOG_DEV (dev), "device added, status: %s, at %s",
bolt_status_to_string (status), syspath);
bolt_manager_label_device (mgr, dev);
if (bolt_status_is_authorized (status))
manager_maybe_import (mgr, dev);
else if (bolt_domain_has_iommu (domain))
manager_auto_enroll (mgr, dev);
/* if we have a valid dbus connection */
bus = bolt_exported_get_connection (BOLT_EXPORTED (mgr));
if (bus == NULL)
return;
opath = bolt_device_export (dev, bus, &err);
if (opath == NULL)
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("dbus"), "error exporting");
else
bolt_info (LOG_DEV (dev), LOG_TOPIC ("dbus"),
"exported device at %.43s...", opath);
bolt_exported_emit_signal (BOLT_EXPORTED (mgr),
"DeviceAdded",
g_variant_new ("(o)", opath),
NULL);
}
static void
handle_udev_device_changed (BoltManager *mgr,
BoltDevice *dev,
struct udev_device *udev)
{
BoltStatus after;
BoltStatus before;
before = bolt_device_get_status (dev);
after = bolt_device_update_from_udev (dev, udev);
bolt_info (LOG_DEV (dev), LOG_TOPIC ("udev"),
"device changed: %s -> %s",
bolt_status_to_string (before),
bolt_status_to_string (after));
/* reaction to status changes of the device, i.e.
* authorizing the children are done in the
* handle_device_status_changed () signal handler
*/
}
static void
handle_udev_device_removed (BoltManager *mgr,
BoltDevice *dev)
{
const char *syspath;
syspath = bolt_device_get_syspath (dev);
bolt_msg (LOG_DEV (dev), "removed (%s)", syspath);
manager_deregister_device (mgr, dev);
}
static void
handle_udev_device_attached (BoltManager *mgr,
BoltDomain *domain,
BoltDevice *dev,
struct udev_device *udev)
{
g_autoptr(BoltDevice) parent = NULL;
const char *syspath;
BoltStatus status;
syspath = udev_device_get_syspath (udev);
status = bolt_device_connected (dev, domain, udev);
bolt_msg (LOG_DEV (dev), "connected: %s (%s)",
bolt_status_to_string (status), syspath);
if (status != BOLT_STATUS_CONNECTED)
return;
parent = bolt_manager_get_parent (mgr, dev);
if (parent)
{
const char *pid = bolt_device_get_uid (parent);
status = bolt_device_get_status (parent);
if (!bolt_status_is_authorized (status))
{
bolt_info (LOG_DEV (dev), "parent [%s] not authorized", pid);
return;
}
}
else
{
bolt_warn (LOG_DEV (dev), "could not find parent");
}
manager_auto_authorize (mgr, dev);
}
static void
handle_udev_device_detached (BoltManager *mgr,
BoltDevice *dev)
{
const char *syspath;
syspath = bolt_device_get_syspath (dev);
bolt_msg (LOG_DEV (dev), "disconnected (%s)", syspath);
bolt_device_disconnected (dev);
}
static void
handle_store_device_added (BoltStore *store,
const char *uid,
BoltManager *mgr)
{
g_autoptr(BoltDevice) dev = NULL;
BoltDomain *dom = mgr->domains;
dev = manager_find_device_by_uid (mgr, uid, NULL);
if (dev == NULL || dom == NULL)
return;
if (bolt_device_get_policy (dev) != BOLT_POLICY_AUTO)
{
bolt_info (LOG_TOPIC ("bootacl"),
LOG_DEV_UID (uid),
"policy not 'auto', not adding");
return;
}
bolt_domain_foreach (dom, bolt_bootacl_add, dev);
}
static void
handle_store_device_removed (BoltStore *store,
const char *uid,
BoltManager *mgr)
{
g_autoptr(BoltDevice) dev = NULL;
BoltDomain *dom = mgr->domains;
BoltStatus status;
dev = manager_find_device_by_uid (mgr, uid, NULL);
if (!dev)
return;
bolt_msg (LOG_DEV (dev), "removed from store");
/* TODO: maybe move to a new bolt_device_removed (dev) */
g_object_set (dev,
"store", NULL,
"key", BOLT_KEY_MISSING,
"policy", BOLT_POLICY_DEFAULT,
NULL);
/* remove device from bootacl */
bolt_domain_foreach (dom, bolt_bootacl_del, dev);
status = bolt_device_get_status (dev);
/* if the device is connected, keep it around */
if (status != BOLT_STATUS_DISCONNECTED)
return;
manager_deregister_device (mgr, dev);
}
static void
handle_domain_security_changed (BoltManager *mgr,
GParamSpec *unused,
BoltDomain *domain)
{
BoltSecurity security = bolt_domain_get_security (domain);
gboolean online = bolt_domain_is_connected (domain);
if (online)
manager_maybe_set_security (mgr, security);
}
static void
handle_device_status_changed (BoltDevice *dev,
BoltStatus old,
BoltManager *mgr)
{
g_autoptr(GPtrArray) children = NULL;
BoltStatus now;
now = bolt_device_get_status (dev);
bolt_debug (LOG_DEV (dev),
"status changed: %s -> %s",
bolt_status_to_string (old),
bolt_status_to_string (now));
if (now == old)
return; /* sanity check */
if (now == BOLT_STATUS_AUTHORIZING)
mgr->authorizing += 1;
else if (old == BOLT_STATUS_AUTHORIZING)
mgr->authorizing -= 1;
manager_probing_activity (mgr, !mgr->authorizing);
if (now != BOLT_STATUS_AUTHORIZED)
return;
/* see if the new status changes anything for the
* children, e.g. the can now be authorized */
children = bolt_manager_get_children (mgr, dev);
for (guint i = 0; i < children->len; i++)
{
BoltDevice *child = g_ptr_array_index (children, i);
if (bolt_device_get_stored (child))
manager_auto_authorize (mgr, child);
else if (bolt_device_has_iommu (child))
manager_auto_enroll (mgr, child);
}
}
static void
handle_device_generation_changed (BoltDevice *dev,
GParamSpec *unused,
BoltManager *mgr)
{
guint gen = bolt_device_get_generation (dev);
bolt_debug (LOG_DEV (dev), LOG_TOPIC ("generation"),
"updated to: %u", gen);
if (!bolt_device_is_host (dev))
return;
manager_maybe_set_generation (mgr, gen);
}
static void
handle_power_state_changed (GObject *gobject,
GParamSpec *pspec,
gpointer user_data)
{
BoltManager *mgr = BOLT_MANAGER (user_data);
gboolean supported = bolt_power_can_force (mgr->power);
BoltPowerState state = bolt_power_get_state (mgr->power);
bolt_info (LOG_TOPIC ("power"), "state changed: %s/%s",
(supported ? "supported" : "unsupported"),
bolt_power_state_to_string (state));
g_object_notify_by_pspec (G_OBJECT (mgr), props[PROP_POWERSTATE]);
manager_sd_notify_status (mgr);
}
static gboolean
probing_timeout (gpointer user_data)
{
BoltManager *mgr;
gint64 now, dt, timeout;
mgr = BOLT_MANAGER (user_data);
if (mgr->authorizing > 0)
return G_SOURCE_CONTINUE;
now = g_get_monotonic_time ();
dt = now - mgr->probing_tstamp;
/* dt is in microseconds, probing timeout in
* milli seconds */
timeout = mgr->probing_tsettle * BOLT_USEC_PER_MSEC;
if (dt < timeout)
return G_SOURCE_CONTINUE;
/* we are done, remove us */
mgr->probing_timeout = 0;
g_object_notify_by_pspec (G_OBJECT (mgr), props[PROP_PROBING]);
bolt_info (LOG_TOPIC ("probing"), "timeout, done: [%ld] (%ld)", dt, timeout);
return G_SOURCE_REMOVE;
}
static void
manager_probing_activity (BoltManager *mgr,
gboolean weak)
{
guint dt;
mgr->probing_tstamp = g_get_monotonic_time ();
if (mgr->probing_timeout || weak)
return;
dt = mgr->probing_tsettle / 2;
bolt_info (LOG_TOPIC ("probing"), "started [%u]", dt);
mgr->probing_timeout = g_timeout_add (dt, probing_timeout, mgr);
g_object_notify_by_pspec (G_OBJECT (mgr), props[PROP_PROBING]);
}
static gboolean
device_is_thunderbolt_root (struct udev_device *dev)
{
const char *driver;
const char *subsys;
driver = udev_device_get_driver (dev);
subsys = udev_device_get_subsystem (dev);
return bolt_streq (subsys, "pci") &&
bolt_streq (driver, "thunderbolt");
}
static gboolean
device_is_wakeup (struct udev_device *dev)
{
const char *subsys;
subsys = udev_device_get_subsystem (dev);
return bolt_streq (subsys, "wakeup");
}
static gboolean
probing_add_root (BoltManager *mgr,
struct udev_device *dev)
{
const char *syspath;
GPtrArray *roots;
g_return_val_if_fail (device_is_thunderbolt_root (dev), FALSE);
/* we go two levels up */
for (guint i = 0; dev != NULL && i < 2; i++)
dev = udev_device_get_parent (dev);
if (dev == NULL)
return FALSE;
roots = mgr->probing_roots;
syspath = udev_device_get_syspath (dev);
g_ptr_array_add (roots, g_strdup (syspath));
bolt_info (LOG_TOPIC ("probing"), "adding %s to roots", syspath);
return TRUE;
}
static void
manager_probing_device_added (BoltManager *mgr,
struct udev_device *dev)
{
const char *syspath;
GPtrArray *roots;
gboolean added;
syspath = udev_device_get_syspath (dev);
if (syspath == NULL)
return;
/* ignore events for wakeup devices which get removed
* and added at random time without any connection to
* any physical thunderbolt device */
if (device_is_wakeup (dev))
return;
roots = mgr->probing_roots;
for (guint i = 0; i < roots->len; i++)
{
const char *r = g_ptr_array_index (roots, i);
if (g_str_has_prefix (syspath, r))
{
bolt_debug (LOG_TOPIC ("probing"), "match %s", syspath);
/* do something */
manager_probing_activity (mgr, FALSE);
return;
}
}
/* if we ended up here we didn't find a root,
* maybe we are one
*/
if (!device_is_thunderbolt_root (dev))
return;
added = probing_add_root (mgr, dev);
if (added)
manager_probing_activity (mgr, FALSE);
}
static void
manager_probing_device_removed (BoltManager *mgr,
struct udev_device *dev)
{
const char *syspath;
GPtrArray *roots;
gboolean found;
guint index;
syspath = udev_device_get_syspath (dev);
if (syspath == NULL)
return;
roots = mgr->probing_roots;
found = FALSE;
for (index = 0; index < roots->len; index++)
{
const char *r = g_ptr_array_index (roots, index);
found = g_str_equal (syspath, r);
if (found)
break;
}
if (!found)
return;
bolt_info (LOG_TOPIC ("probing"), "removing %s from roots", syspath);
g_ptr_array_remove_index_fast (mgr->probing_roots, index);
}
static void
manager_probing_domain_added (BoltManager *mgr,
struct udev_device *domain)
{
struct udev_device *p = domain;
/* walk up until we find the thunderbolt root */
while (p && !device_is_thunderbolt_root (p))
p = udev_device_get_parent (p);
if (p == NULL)
return;
probing_add_root (mgr, p);
}
static BoltGuard *
manager_maybe_power_controller (BoltManager *mgr)
{
g_autoptr(GError) err = NULL;
BoltGuard *guard = NULL;
gboolean can_force_power;
int n;
can_force_power = bolt_power_can_force (mgr->power);
if (can_force_power == FALSE)
return NULL;
n = bolt_udev_count_hosts (mgr->udev, &err);
if (n < 0)
{
bolt_warn_err (err, LOG_TOPIC ("udev"),
"failed to count domains");
return NULL;
}
else if (n > 0)
{
goto out;
}
guard = bolt_power_acquire (mgr->power, &err);
if (guard == NULL)
{
bolt_warn_err (err, LOG_TOPIC ("power"),
"could not force power");
return NULL;
}
/* we wait for a total of 5.0 seconds, should hopefully
* be enough for at least the domain to show up. */
for (int i = 0; i < 25 && n < 1; i++)
{
g_usleep (200000); /* 200 000 us = 0.2s */
n = bolt_udev_count_hosts (mgr->udev, NULL);
}
out:
bolt_info (LOG_TOPIC ("udev"), "found %d domain%s",
n, n > 1 ? "s" : "");
return guard;
}
/* config */
static void
manager_load_user_config (BoltManager *mgr)
{
g_autoptr(GError) err = NULL;
BoltPolicy policy;
BoltAuthMode authmode;
BoltTri res;
bolt_info (LOG_TOPIC ("config"), "loading user config");
mgr->config = bolt_store_config_load (mgr->store, &err);
if (mgr->config == NULL)
{
if (!bolt_err_notfound (err))
bolt_warn_err (err, LOG_TOPIC ("config"),
"failed to load user config");
return;
}
bolt_info (LOG_TOPIC ("config"), "user config loaded successfully");
res = bolt_config_load_default_policy (mgr->config, &policy, &err);
if (res == TRI_ERROR)
{
bolt_warn_err (err, LOG_TOPIC ("config"),
"failed to load default policy");
g_clear_error (&err);
}
else if (res == TRI_YES)
{
mgr->policy = policy;
bolt_info (LOG_TOPIC ("config"), "default policy set to %s",
bolt_policy_to_string (policy));
g_object_notify_by_pspec (G_OBJECT (mgr), props[PROP_POLICY]);
}
res = bolt_config_load_auth_mode (mgr->config, &authmode, &err);
if (res == TRI_ERROR)
{
bolt_warn_err (err, LOG_TOPIC ("config"),
"failed to load auth mode");
g_clear_error (&err);
}
else if (res == TRI_YES)
{
g_autofree char *str = NULL;
str = bolt_flags_to_string (BOLT_TYPE_AUTH_MODE, authmode, NULL);
bolt_info (LOG_TOPIC ("config"), "auth mode set to '%s'", str);
mgr->authmode = authmode;
g_object_notify_by_pspec (G_OBJECT (mgr), props[PROP_POLICY]);
}
}
/* dbus property setter */
static gboolean
handle_set_authmode (BoltExported *obj,
const char *name,
const GValue *value,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *str = NULL;
BoltManager *mgr = BOLT_MANAGER (obj);
BoltAuthMode authmode;
gboolean ok;
authmode = g_value_get_flags (value);
if (authmode == mgr->authmode)
return TRUE;
if (mgr->config == NULL)
mgr->config = bolt_config_user_init ();
str = bolt_flags_to_string (BOLT_TYPE_AUTH_MODE, authmode, &err);
if (str == NULL)
{
bolt_warn_err (err, LOG_TOPIC ("config"), "error setting authmode");
return bolt_error_propagate (error, &err);
}
bolt_config_set_auth_mode (mgr->config, str);
ok = bolt_store_config_save (mgr->store, mgr->config, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("config"), "error saving config");
return bolt_error_propagate (error, &err);
}
mgr->authmode = authmode;
bolt_info (LOG_TOPIC ("config"), "auth mode set to '%s'", str);
manager_sd_notify_status (mgr);
return ok;
}
/* dbus methods: domain related */
static GVariant *
handle_list_domains (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error)
{
BoltManager *mgr = BOLT_MANAGER (object);
const char **domains;
BoltDomain *iter;
guint count;
count = bolt_domain_count (mgr->domains);
domains = g_newa (const char *, count + 1);
iter = mgr->domains;
for (guint i = 0; i < count; i++)
{
domains[i] = bolt_exported_get_object_path (BOLT_EXPORTED (iter));
iter = bolt_domain_next (iter);
}
domains[count] = NULL;
return g_variant_new ("(^ao)", domains);
}
static GVariant *
handle_domain_by_id (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error)
{
BoltManager *mgr = BOLT_MANAGER (object);
BoltDomain *domain;
const char *op;
const char *id;
g_variant_get (params, "(&s)", &id);
if (id == NULL || id[0] == '\0')
{
g_set_error_literal (error, G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"empty domain id");
return NULL;
}
domain = bolt_domain_find_id (mgr->domains, id, error);
if (domain == NULL)
return NULL;
op = bolt_exported_get_object_path (BOLT_EXPORTED (domain));
return g_variant_new ("(o)", op);
}
/* dbus methods: device related */
static GVariant *
handle_list_devices (BoltExported *obj,
GVariant *params,
GDBusMethodInvocation *inv,
GError **error)
{
BoltManager *mgr = BOLT_MANAGER (obj);
const char **devs;
devs = g_newa (const char *, mgr->devices->len + 1);
for (guint i = 0; i < mgr->devices->len; i++)
{
BoltDevice *d = g_ptr_array_index (mgr->devices, i);
devs[i] = bolt_device_get_object_path (d);
}
devs[mgr->devices->len] = NULL;
return g_variant_new ("(^ao)", devs);
}
static GVariant *
handle_device_by_uid (BoltExported *obj,
GVariant *params,
GDBusMethodInvocation *inv,
GError **error)
{
g_autoptr(BoltDevice) dev = NULL;
BoltManager *mgr;
const char *uid;
const char *opath;
mgr = BOLT_MANAGER (obj);
g_variant_get (params, "(&s)", &uid);
dev = manager_find_device_by_uid (mgr, uid, error);
if (dev == NULL)
return NULL;
opath = bolt_device_get_object_path (dev);
return g_variant_new ("(o)", opath);
}
static void
enroll_device_done (GObject *device,
GAsyncResult *res,
gpointer user_data)
{
GDBusMethodInvocation *inv = user_data;
BoltDevice *dev = BOLT_DEVICE (device);
BoltAuth *auth = BOLT_AUTH (res);
GError *error = NULL;
BoltManager *mgr;
const char *opath;
gboolean ok;
mgr = BOLT_MANAGER (bolt_auth_get_origin (auth));
ok = bolt_auth_check (auth, &error);
if (ok)
{
ok = bolt_store_put_device (mgr->store,
dev,
bolt_auth_get_policy (auth),
bolt_auth_get_key (auth),
&error);
}
if (!ok)
{
g_dbus_method_invocation_take_error (inv, error);
return;
}
opath = bolt_device_get_object_path (dev);
g_dbus_method_invocation_return_value (inv, g_variant_new ("(o)", opath));
}
static GVariant *
enroll_device_store_authorized (BoltManager *mgr,
BoltDevice *dev,
BoltPolicy policy,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(BoltKey) key = NULL;
const char *opath;
gboolean ok;
bolt_info (LOG_DEV (dev), "enrolling an authorized device (%s)",
bolt_policy_to_string (policy));
ok = bolt_device_get_key_from_sysfs (dev, &key, &err);
if (!ok)
{
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("udev"),
"failed to read key from sysfs");
g_propagate_prefixed_error (error, g_steal_pointer (&err), "%s",
"could not determine existing authorization: ");
return NULL;
}
ok = bolt_store_put_device (mgr->store, dev, policy, key, &err);
if (!ok)
{
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("store"),
"failed to store device");
bolt_error_propagate (error, &err);
return NULL;
}
opath = bolt_device_get_object_path (dev);
return g_variant_new ("(o)", opath);
}
static GVariant *
handle_enroll_device (BoltExported *obj,
GVariant *params,
GDBusMethodInvocation *inv,
GError **error)
{
g_autoptr(BoltDevice) dev = NULL;
g_autoptr(BoltAuth) auth = NULL;
BoltManager *mgr;
const char *uid;
BoltPolicy pol;
const char *policy;
mgr = BOLT_MANAGER (obj);
g_variant_get_child (params, 0, "&s", &uid);
g_variant_get_child (params, 1, "&s", &policy);
dev = manager_find_device_by_uid (mgr, uid, error);
if (dev == NULL)
return NULL;
pol = bolt_enum_from_string (BOLT_TYPE_POLICY, policy, error);
if (pol == BOLT_POLICY_UNKNOWN)
{
if (*error == NULL)
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"invalid policy: %s", policy);
return NULL;
}
else if (pol == BOLT_POLICY_DEFAULT)
{
if (bolt_device_has_iommu (dev))
pol = BOLT_POLICY_IOMMU;
else
pol = mgr->policy;
bolt_info (LOG_DEV (dev), LOG_TOPIC ("enroll"),
"got 'default' policy, adjusted to: '%s'",
bolt_policy_to_string (pol));
}
if (bolt_device_get_stored (dev))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
"device with id '%s' already enrolled.",
uid);
return NULL;
}
/* if the device is already authorized, we just store it */
if (bolt_device_is_authorized (dev))
return enroll_device_store_authorized (mgr, dev, pol, error);
auth = manager_enroll_device_prepare (mgr, dev, error);
if (auth == NULL)
return NULL;
bolt_auth_set_policy (auth, pol);
bolt_device_authorize (dev, auth, enroll_device_done, inv);
return NULL;
}
static GVariant *
handle_forget_device (BoltExported *obj,
GVariant *params,
GDBusMethodInvocation *inv,
GError **error)
{
g_autoptr(BoltDevice) dev = NULL;
BoltManager *mgr;
gboolean ok;
const char *uid;
mgr = BOLT_MANAGER (obj);
g_variant_get (params, "(&s)", &uid);
dev = manager_find_device_by_uid (mgr, uid, error);
if (dev == NULL)
return FALSE;
ok = bolt_store_del (mgr->store, dev, error);
return ok ? g_variant_new ("()") : NULL;
}
/* public methods */
gboolean
bolt_manager_export (BoltManager *mgr,
GDBusConnection *connection,
GError **error)
{
g_autoptr(GError) err = NULL;
gboolean ok;
if (!bolt_exported_export (BOLT_EXPORTED (mgr),
connection,
BOLT_DBUS_PATH,
error))
return FALSE;
ok = bolt_exported_export (BOLT_EXPORTED (mgr->power),
connection,
BOLT_DBUS_PATH,
error);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("dbus"),
"failed to export power object");
g_clear_error (&err);
}
bolt_domain_foreach (mgr->domains,
(GFunc) bolt_domain_export,
connection);
for (guint i = 0; i < mgr->devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (mgr->devices, i);
const char *opath;
opath = bolt_device_export (dev, connection, &err);
if (opath == NULL)
{
bolt_warn_err (err, LOG_DEV (dev), LOG_TOPIC ("dbus"),
"error exporting a device");
g_clear_error (&err);
continue;
}
bolt_info (LOG_DEV (dev), LOG_TOPIC ("dbus"),
"exported device at %.43s...", opath);
}
return TRUE;
}
void
bolt_manager_got_the_name (BoltManager *mgr)
{
/* emit DeviceAdded signals now that we have the name
* for all devices that are not stored and connected
*/
for (guint i = 0; i < mgr->devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (mgr->devices, i);
BoltStatus status;
gboolean stored;
const char *opath;
stored = bolt_device_get_stored (dev);
if (stored)
continue;
status = bolt_device_get_status (dev);
if (status != BOLT_STATUS_CONNECTED)
continue;
opath = bolt_exported_get_object_path (BOLT_EXPORTED (dev));
if (opath == NULL)
continue;
bolt_exported_emit_signal (BOLT_EXPORTED (mgr),
"DeviceAdded",
g_variant_new ("(o)", opath),
NULL);
}
}
bolt-0.9.2/boltd/bolt-manager.h 0000664 0000000 0000000 00000002324 14174530510 0016310 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-exported.h"
G_BEGIN_DECLS
#define BOLT_TYPE_MANAGER bolt_manager_get_type ()
G_DECLARE_FINAL_TYPE (BoltManager, bolt_manager, BOLT, MANAGER, BoltExported);
gboolean bolt_manager_export (BoltManager *mgr,
GDBusConnection *connection,
GError **error);
void bolt_manager_got_the_name (BoltManager *mgr);
G_END_DECLS
bolt-0.9.2/boltd/bolt-power.c 0000664 0000000 0000000 00000061551 14174530510 0016034 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-power.h"
#include "bolt-config.h"
#include "bolt-dbus.h"
#include "bolt-enums.h"
#include "bolt-error.h"
#include "bolt-fs.h"
#include "bolt-log.h"
#include "bolt-io.h"
#include "bolt-reaper.h"
#include "bolt-str.h"
#include "bolt-unix.h"
#include
#include
#include
#include
#define POWER_WAIT_TIMEOUT 20 * 1000 // 20 seconds
#define POWER_REAPER_TIMEOUT 20 // seconds
#define DEFAULT_STATEDIR "power"
#define STATE_FILENAME "on"
/* prototypes */
static void bolt_power_release (BoltPower *power,
BoltGuard *guard);
/* GObject */
static void power_initable_iface_init (GInitableIface *iface);
static gboolean bolt_power_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error);
/* utilities */
static char * bolt_power_gen_guard_id (BoltPower *power,
GError **error);
static gboolean bolt_power_recover_guards (BoltPower *power,
GError **error);
static void bolt_power_timeout_reset (BoltPower *power);
static gboolean bolt_power_switch_toggle (BoltPower *power,
gboolean on,
GError **error);
/* callbacks and signals */
static gboolean bolt_power_wait_timeout (gpointer user_data);
static void handle_uevent_udev (BoltUdev *udev,
const char *action,
struct udev_device *device,
gpointer user_data);
/* dbus methods */
static GVariant * handle_list_guards (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error);
static GVariant * handle_force_power (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error);
struct _BoltPower
{
BoltExported object;
/* path to store run time data */
char *runpath;
GFile *statedir;
GFile *statefile;
/* connection to udev */
BoltUdev *udev;
/* the path to the sysfs device file,
* or NULL if force power is unavailable */
char *path;
BoltPowerState state;
/* */
guint16 guard_num;
GHashTable *guards;
/* wait before off handling */
guint wait_id;
guint timeout; /* milliseconds */
};
enum {
PROP_0,
PROP_RUNDIR,
PROP_STATEDIR,
PROP_UDEV,
PROP_SUPPORTED,
PROP_STATE,
PROP_TIMEOUT,
PROP_LAST
};
static GParamSpec *power_props[PROP_LAST] = { NULL, };
G_DEFINE_TYPE_WITH_CODE (BoltPower,
bolt_power,
BOLT_TYPE_EXPORTED,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
power_initable_iface_init));
static void
bolt_power_finalize (GObject *object)
{
BoltPower *power = BOLT_POWER (object);
if (power->wait_id != 0)
{
g_source_remove (power->wait_id);
bolt_power_wait_timeout (power);
}
g_clear_pointer (&power->runpath, g_free);
g_clear_object (&power->statedir);
g_clear_object (&power->statefile);
g_clear_object (&power->udev);
g_clear_pointer (&power->path, g_free);
g_clear_pointer (&power->guards, g_hash_table_unref);
G_OBJECT_CLASS (bolt_power_parent_class)->finalize (object);
}
static void
bolt_power_init (BoltPower *power)
{
power->state = BOLT_FORCE_POWER_UNSET;
power->guards = g_hash_table_new (g_str_hash, g_str_equal);
}
static void
bolt_power_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltPower *power = BOLT_POWER (object);
switch (prop_id)
{
case PROP_RUNDIR:
g_value_set_string (value, power->runpath);
break;
case PROP_STATEDIR:
g_value_set_object (value, power->statedir);
break;
case PROP_UDEV:
g_value_set_object (value, power->udev);
break;
case PROP_SUPPORTED:
g_value_set_boolean (value, power->path != NULL);
break;
case PROP_STATE:
g_value_set_enum (value, power->state);
break;
case PROP_TIMEOUT:
g_value_set_uint (value, power->timeout);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_power_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltPower *power = BOLT_POWER (object);
switch (prop_id)
{
case PROP_RUNDIR:
power->runpath = g_value_dup_string (value);
break;
case PROP_UDEV:
power->udev = g_value_dup_object (value);
break;
case PROP_TIMEOUT:
power->timeout = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_power_constructed (GObject *obj)
{
BoltPower *power = BOLT_POWER (obj);
G_OBJECT_CLASS (bolt_power_parent_class)->constructed (obj);
if (power->runpath == NULL)
power->runpath = g_strdup (bolt_get_runtime_directory ());
}
static void
bolt_power_class_init (BoltPowerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
BoltExportedClass *exported_class = BOLT_EXPORTED_CLASS (klass);
gobject_class->finalize = bolt_power_finalize;
gobject_class->constructed = bolt_power_constructed;
gobject_class->get_property = bolt_power_get_property;
gobject_class->set_property = bolt_power_set_property;
power_props[PROP_RUNDIR] =
g_param_spec_string ("rundir",
NULL, NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
power_props[PROP_STATEDIR] =
g_param_spec_object ("statedir",
NULL, NULL,
G_TYPE_FILE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
power_props[PROP_UDEV] =
g_param_spec_object ("udev",
NULL, NULL,
BOLT_TYPE_UDEV,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
power_props[PROP_SUPPORTED] =
g_param_spec_boolean ("supported",
"Supported", NULL,
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_NICK);
power_props[PROP_STATE] =
g_param_spec_enum ("state",
"State", NULL,
BOLT_TYPE_POWER_STATE,
BOLT_FORCE_POWER_UNSET,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
power_props[PROP_TIMEOUT] =
g_param_spec_uint ("timeout",
"Timeout", NULL,
0, G_MAXINT, POWER_WAIT_TIMEOUT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
power_props);
bolt_exported_class_set_interface_info (exported_class,
BOLT_DBUS_POWER_INTERFACE,
BOLT_DBUS_GRESOURCE_PATH);
bolt_exported_class_export_properties (exported_class,
PROP_SUPPORTED,
PROP_LAST,
power_props);
bolt_exported_class_export_method (exported_class,
"ForcePower",
handle_force_power);
bolt_exported_class_export_method (exported_class,
"ListGuards",
handle_list_guards);
}
static void
power_initable_iface_init (GInitableIface *iface)
{
iface->init = bolt_power_initialize;
}
static gboolean
bolt_power_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(BoltGuard) guard = NULL;
g_autofree char *statedir = NULL;
BoltPower *power = BOLT_POWER (initable);
gboolean on = FALSE;
gboolean ok;
guint guards;
statedir = g_build_filename (power->runpath, DEFAULT_STATEDIR, NULL);
power->statedir = g_file_new_for_path (statedir);
power->statefile = g_file_get_child (power->statedir, STATE_FILENAME);
bolt_info (LOG_TOPIC ("power"), "state located at: %s", statedir);
ok = g_file_make_directory_with_parents (power->statedir, NULL, &err);
if (!ok && !bolt_err_exists (err))
bolt_warn_err (err, LOG_TOPIC ("power"),
"failed to create guarddir at %s", statedir);
g_clear_error (&err);
g_signal_connect_object (power->udev, "uevent",
(GCallback) handle_uevent_udev,
power, 0);
ok = bolt_udev_detect_force_power (power->udev, &power->path, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("power"),
"failure while detecting force power");
g_clear_error (&err);
}
bolt_msg (LOG_TOPIC ("power"), "force power support: %s",
bolt_yesno (power->path != NULL));
if (power->path == NULL)
return TRUE;
/* recover force power state */
on = g_file_query_exists (power->statefile, NULL);
/* recover saved power guards */
ok = bolt_power_recover_guards (power, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("power"),
"failed to recover guards");
g_clear_error (&err);
/* NOT a critical failure */
}
/* enforce that our ON state is actually true */
guards = g_hash_table_size (power->guards);
if (on || guards > 0)
{
bolt_msg (LOG_TOPIC ("power"), "recovered state, on: %s, guards: %u",
bolt_yesno (on), guards);
bolt_info (LOG_TOPIC ("power"), "creating temporary power guard");
guard = bolt_power_acquire (power, &err);
if (guard == NULL)
bolt_warn_err (err, LOG_TOPIC ("power"),
"failed to force-power controller");
/* failures here are not critical */
}
return TRUE;
}
/* internal methods */
static gboolean
bolt_power_recover_guards (BoltPower *power,
GError **error)
{
g_autoptr(GPtrArray) guards = NULL;
g_autofree char *statedir = NULL;
statedir = g_file_get_path (power->statedir);
guards = bolt_guard_recover (statedir, error);
if (guards == NULL)
return FALSE;
for (guint i = 0; i < guards->len; i++)
{
BoltGuard *guard = g_ptr_array_index (guards, i);
const char *id = bolt_guard_get_id (guard);
const char *who = bolt_guard_get_who (guard);
const guint pid = bolt_guard_get_pid (guard);
bolt_info (LOG_TOPIC ("power"),
"guard '%s' for '%s' (pid %u) recovered",
id, who, pid);
g_signal_connect_object (guard, "released",
(GCallback) bolt_power_release,
power, G_CONNECT_SWAPPED);
g_hash_table_insert (power->guards,
(gpointer) bolt_guard_get_id (guard),
(gpointer) g_object_ref (guard));
}
return TRUE;
}
static gboolean
bolt_power_wait_timeout (gpointer user_data)
{
g_autoptr(GError) err = NULL;
BoltPower *power = user_data;
gboolean ok;
if (power->path == NULL)
/* force power support got removed while being used,
* this was already complained about, so ignore it */
return G_SOURCE_REMOVE;
/* we just removed the last active guard */
ok = bolt_power_switch_toggle (power, FALSE, &err);
if (!ok)
bolt_warn_err (err, LOG_TOPIC ("power"),
"failed to turn off force_power");
power->wait_id = 0;
return G_SOURCE_REMOVE;
}
static void
handle_uevent_thunderbolt (BoltPower *power,
const char *action,
struct udev_device *device)
{
/* no callback scheduled, nothing to do */
if (power->wait_id == 0)
return;
/* only interested in added devices */
if (!bolt_streq (action, "add"))
return;
/* if we are not in WAIT state, we don't
* do anything, but if we are, we want
* to reset the timeout */
if (power->state != BOLT_FORCE_POWER_WAIT)
return;
bolt_info (LOG_TOPIC ("power"), "resetting timeout (uevent %s)",
udev_device_get_syspath (device));
bolt_power_timeout_reset (power);
}
static void
handle_uevent_wmi (BoltPower *power,
const char *action,
struct udev_device *device)
{
g_autoptr(GError) err = NULL;
g_autofree char *path = NULL;
const char *name;
const char *syspath;
gboolean changed = FALSE;
gboolean ok;
syspath = udev_device_get_syspath (device);
name = udev_device_get_sysname (device);
bolt_debug (LOG_TOPIC ("power"), "uevent: wmi %s %s [%s %s]",
action, name, syspath, power->path ? : "");
if (!bolt_streq (action, "change"))
return;
ok = bolt_udev_detect_force_power (power->udev, &path, &err);
if (!ok)
{
bolt_warn_err (err, LOG_TOPIC ("udev"),
"failed to detect force_power support");
g_clear_error (&err);
}
if (path != NULL)
{
if (power->path == NULL || !bolt_streq (path, power->path))
{
bolt_set_str (&power->path, g_steal_pointer (&path));
changed = TRUE;
}
}
else if (path == NULL && power->path != NULL)
{
if (power->state > BOLT_FORCE_POWER_OFF)
bolt_warn (LOG_TOPIC ("power"),
"force power supported removed while active");
g_clear_pointer (&power->path, g_free);
changed = TRUE;
}
if (changed)
{
/* if changed, we don't know our current state */
power->state = BOLT_FORCE_POWER_UNSET;
g_object_notify_by_pspec (G_OBJECT (power),
power_props[PROP_STATE]);
g_object_notify_by_pspec (G_OBJECT (power),
power_props[PROP_SUPPORTED]);
}
}
static void
handle_uevent_udev (BoltUdev *udev,
const char *action,
struct udev_device *device,
gpointer user_data)
{
BoltPower *power = BOLT_POWER (user_data);
const char *subsystem;
subsystem = udev_device_get_subsystem (device);
if (bolt_streq (subsystem, "thunderbolt"))
handle_uevent_thunderbolt (power, action, device);
else if (bolt_streq (subsystem, "wmi"))
handle_uevent_wmi (power, action, device);
}
static void
bolt_power_timeout_reset (BoltPower *power)
{
if (power->wait_id > 0)
g_source_remove (power->wait_id);
power->wait_id = g_timeout_add (power->timeout,
bolt_power_wait_timeout,
power);
if (power->state != BOLT_FORCE_POWER_WAIT)
{
power->state = BOLT_FORCE_POWER_WAIT;
g_object_notify_by_pspec (G_OBJECT (power),
power_props[PROP_STATE]);
}
}
static gboolean
bolt_power_switch_toggle (BoltPower *power,
gboolean on,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *statepath = NULL;
BoltPowerState state;
gboolean ok;
int fd;
g_return_val_if_fail (BOLT_IS_POWER (power), FALSE);
g_return_val_if_fail (power->path != NULL, FALSE);
bolt_info (LOG_TOPIC ("power"), "setting force_power to %s",
on ? "ON" : "OFF");
fd = bolt_open (power->path, O_WRONLY, 0, error);
if (fd < 0)
return FALSE;
ok = bolt_write_all (fd, on ? "1" : "0", 1, error);
bolt_close (fd, NULL);
if (!ok)
return FALSE;
statepath = g_file_get_path (power->statefile);
if (on)
{
state = BOLT_FORCE_POWER_ON;
fd = bolt_open (statepath, O_CREAT | O_TRUNC, 0666, &err);
ok = fd > -1;
if (ok)
(void) close (fd);
}
else
{
state = BOLT_FORCE_POWER_OFF;
ok = bolt_unlink (statepath, &err);
}
if (!ok)
bolt_warn_err (err, "could not write force_power state-file");
else
bolt_debug (LOG_TOPIC ("power"), "wrote state %s to %s",
bolt_power_state_to_string (state),
statepath);
power->state = state;
g_object_notify_by_pspec (G_OBJECT (power),
power_props[PROP_STATE]);
return ok;
}
static char *
bolt_power_gen_guard_id (BoltPower *power,
GError **error)
{
char *id = NULL;
if (g_hash_table_size (power->guards) >= G_MAXUINT16)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"maximum number of force power locks reached");
return NULL;
}
do
{
guint16 i = ++power->guard_num;
g_free (id);
id = g_strdup_printf ("%" G_GUINT16_FORMAT, i);
}
while (g_hash_table_contains (power->guards, id));
return id;
}
static void
bolt_power_release (BoltPower *power, BoltGuard *guard)
{
const char *id;
const char *who;
gboolean ok;
id = bolt_guard_get_id (guard);
who = bolt_guard_get_who (guard);
ok = g_hash_table_remove (power->guards, id);
if (!ok)
{
bolt_bug ("inactive guard ('%s', '%s') found", id, who);
return;
}
bolt_info (LOG_TOPIC ("power"), "guard '%s' for '%s' deactivated",
id, who);
/* we still have active guards */
if (g_hash_table_size (power->guards) != 0)
return;
/* go into WAIT (from ON) state */
if (power->wait_id != 0)
{
bolt_bug ("have active waiter already");
return;
}
if (power->timeout == 0)
{
bolt_info (LOG_TOPIC ("power"), "wait timeout is zero, skipping");
bolt_power_wait_timeout ((gpointer) power);
return;
}
bolt_info (LOG_TOPIC ("power"), "shutdown scheduled (T-%3.2fs)",
power->timeout / 1000.0);
bolt_power_timeout_reset (power);
}
/* dbus methods */
static GVariant *
handle_force_power (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error)
{
g_autoptr(BoltGuard) guard = NULL;
g_autoptr(GUnixFDList) fds = NULL;
BoltPower *power;
const char *flags;
const char *who;
gboolean ok;
guint pid;
int fd;
power = BOLT_POWER (object);
ok = bolt_dbus_get_sender_pid (invocation, &pid, error);
if (!ok)
return NULL;
g_variant_get (params, "(&s&s)", &who, &flags);
guard = bolt_power_acquire_full (power, who, (pid_t) pid, error);
if (guard == NULL)
{
bolt_warn_err (*error, LOG_TOPIC ("power"),
"failed to acquire power for %s (pid %u)",
who, pid);
return NULL;
}
/* monitor will add a reference to guard, so freeing one
* via the auto pointer is expected and in fact desired */
fd = bolt_guard_monitor (guard, error);
if (fd == -1)
{
bolt_warn_err (*error, LOG_TOPIC ("power"),
"failed to monitor guard %s for %s (pid %u)",
bolt_guard_get_id (guard), who, pid);
return NULL;
}
fds = g_unix_fd_list_new_from_array (&fd, 1);
g_dbus_method_invocation_return_value_with_unix_fd_list (invocation,
g_variant_new ("(h)"),
fds);
return NULL;
}
static GVariant *
handle_list_guards (BoltExported *object,
GVariant *params,
GDBusMethodInvocation *invocation,
GError **error)
{
g_autoptr(GList) guards = NULL;
BoltPower *power;
GVariantBuilder b;
power = BOLT_POWER (object);
guards = bolt_power_list_guards (power);
g_variant_builder_init (&b, G_VARIANT_TYPE ("a(ssu)"));
for (GList *l = guards; l != NULL; l = l->next)
{
BoltGuard *g = BOLT_GUARD (l->data);
g_variant_builder_add (&b, "(ssu)",
bolt_guard_get_id (g),
bolt_guard_get_who (g),
bolt_guard_get_pid (g));
}
return g_variant_new ("(a(ssu))", &b);
}
/* public methods */
BoltPower *
bolt_power_new (BoltUdev *udev)
{
BoltPower *power;
power = g_initable_new (BOLT_TYPE_POWER,
NULL, NULL,
"udev", udev,
NULL);
return power;
}
GFile *
bolt_power_get_statedir (BoltPower *power)
{
g_return_val_if_fail (BOLT_IS_POWER (power), FALSE);
return power->statedir;
}
gboolean
bolt_power_can_force (BoltPower *power)
{
g_return_val_if_fail (BOLT_IS_POWER (power), FALSE);
return power->path != NULL;
}
BoltPowerState
bolt_power_get_state (BoltPower *power)
{
g_return_val_if_fail (BOLT_IS_POWER (power), -1);
return power->state;
}
BoltGuard *
bolt_power_acquire (BoltPower *power,
GError **error)
{
const char *who = "boltd";
return bolt_power_acquire_full (power, who, 0, error);
}
BoltGuard *
bolt_power_acquire_full (BoltPower *power,
const char *who,
pid_t pid,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *id = NULL;
BoltGuard *guard;
gboolean ok;
g_return_val_if_fail (BOLT_IS_POWER (power), NULL);
g_return_val_if_fail (who != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (power->path == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"force power not supported");
return FALSE;
}
id = bolt_power_gen_guard_id (power, error);
if (id == NULL)
return NULL;
if (power->state == BOLT_FORCE_POWER_WAIT)
{
g_source_remove (power->wait_id);
power->wait_id = 0;
power->state = BOLT_FORCE_POWER_ON;
g_object_notify_by_pspec (G_OBJECT (power),
power_props[PROP_STATE]);
}
else if (power->state != BOLT_FORCE_POWER_ON)
{
ok = bolt_power_switch_toggle (power, TRUE, &err);
if (!ok)
{
bolt_error_propagate (error, &err);
return NULL;
}
}
if (pid == 0)
pid = getpid ();
guard = g_object_new (BOLT_TYPE_GUARD,
"id", id,
"who", who,
"pid", pid,
NULL);
g_signal_connect_object (guard, "released",
(GCallback) bolt_power_release,
power, G_CONNECT_SWAPPED);
/* NB: we don't take a ref here, because we want the guard to
* act as RAII guard, i.e. when the client releases the last
* reference to the guard, the "released" signal will be
* triggered and thus bolt_power_release() will be called */
g_hash_table_insert (power->guards,
(gpointer) bolt_guard_get_id (guard),
guard);
bolt_info (LOG_TOPIC ("power"), "guard '%s' for '%s' active",
id, who);
/* guard is saved so we can recover our state if we
* were to crash or restarted */
ok = bolt_guard_save (guard, power->statedir, &err);
if (!ok)
bolt_warn_err (err, LOG_TOPIC ("power"),
"could not save guard '%s'", id);
return guard;
}
GList *
bolt_power_list_guards (BoltPower *power)
{
g_return_val_if_fail (BOLT_IS_POWER (power), NULL);
return g_hash_table_get_values (power->guards);
}
bolt-0.9.2/boltd/bolt-power.h 0000664 0000000 0000000 00000003412 14174530510 0016031 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-exported.h"
#include "bolt-guard.h"
#include "bolt-udev.h"
#include
G_BEGIN_DECLS
/* forward declaration */
struct udev;
/* BoltPower */
#define BOLT_TYPE_POWER bolt_power_get_type ()
G_DECLARE_FINAL_TYPE (BoltPower, bolt_power, BOLT, POWER, BoltExported);
BoltPower * bolt_power_new (BoltUdev *udev);
GFile * bolt_power_get_statedir (BoltPower *power);
gboolean bolt_power_can_force (BoltPower *power);
BoltPowerState bolt_power_get_state (BoltPower *power);
BoltGuard * bolt_power_acquire_full (BoltPower *power,
const char *who,
pid_t pid,
GError **error);
BoltGuard * bolt_power_acquire (BoltPower *power,
GError **error);
GList * bolt_power_list_guards (BoltPower *power);
G_END_DECLS
bolt-0.9.2/boltd/bolt-reaper.c 0000664 0000000 0000000 00000013500 14174530510 0016145 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2020 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-reaper.h"
#include "bolt-log.h"
#include "bolt-unix.h"
#define REAPER_TIMEOUT 20 * 1000 // seconds
struct _BoltReaper
{
GObject object;
/* */
guint timeout;
/* */
GHashTable *pids;
guint timeout_id;
};
enum {
PROP_0,
PROP_TIMEOUT,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = { NULL, };
enum {
PROCESS_DIED,
SIGNAL_LAST
};
static guint signals[SIGNAL_LAST] = {0};
G_DEFINE_TYPE (BoltReaper,
bolt_reaper,
G_TYPE_OBJECT);
static void
bolt_reaper_finalize (GObject *object)
{
BoltReaper *reaper = BOLT_REAPER (object);
g_clear_handle_id (&reaper->timeout_id, g_source_remove);
g_hash_table_unref (reaper->pids);
G_OBJECT_CLASS (bolt_reaper_parent_class)->finalize (object);
}
static void
bolt_reaper_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltReaper *reaper = BOLT_REAPER (object);
switch (prop_id)
{
case PROP_TIMEOUT:
g_value_set_uint (value, reaper->timeout);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_reaper_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltReaper *reaper = BOLT_REAPER (object);
switch (prop_id)
{
case PROP_TIMEOUT:
reaper->timeout = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_reaper_init (BoltReaper *reaper)
{
reaper->pids = g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
g_free);
}
static void
bolt_reaper_class_init (BoltReaperClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = bolt_reaper_finalize;
gobject_class->get_property = bolt_reaper_get_property;
gobject_class->set_property = bolt_reaper_set_property;
props[PROP_TIMEOUT] =
g_param_spec_uint ("timeout",
"Timeout", NULL,
0, G_MAXINT, REAPER_TIMEOUT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
signals[PROCESS_DIED] =
g_signal_new ("process-died",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
2, G_TYPE_UINT, G_TYPE_STRING);
}
static gboolean
bolt_reaper_timeout (gpointer user_data)
{
BoltReaper *reaper = BOLT_REAPER (user_data);
GHashTableIter iter;
gpointer key, value;
bolt_debug (LOG_TOPIC ("reaper"), "looking for dead processes");
g_hash_table_iter_init (&iter, reaper->pids);
while (g_hash_table_iter_next (&iter, &key, &value))
{
g_autofree char *name = NULL;
guint pid = GPOINTER_TO_UINT (key);
bolt_debug (LOG_TOPIC ("reaper"), "checking '%u'", pid);
if (bolt_pid_is_alive ((pid_t) pid))
continue;
bolt_info (LOG_TOPIC ("reaper"),
"process '%u' is dead",
pid);
name = (char *) value;
g_hash_table_iter_steal (&iter);
g_signal_emit (reaper,
signals[PROCESS_DIED],
0,
pid, name);
}
if (g_hash_table_size (reaper->pids) == 0)
{
bolt_debug (LOG_TOPIC ("reaper"), "stopping");
reaper->timeout_id = 0;
return G_SOURCE_REMOVE;
}
return G_SOURCE_CONTINUE;
}
BoltReaper *
bolt_reaper_new (void)
{
BoltReaper *reaper;
reaper = g_object_new (BOLT_TYPE_REAPER, NULL);
return reaper;
}
void
bolt_reaper_add_pid (BoltReaper *reaper,
guint pid,
const char *name)
{
gpointer p = GUINT_TO_POINTER (pid);
g_return_if_fail (BOLT_IS_REAPER (reaper));
g_hash_table_insert (reaper->pids, p, g_strdup (name));
if (reaper->timeout_id != 0)
return;
reaper->timeout_id = g_timeout_add (reaper->timeout,
bolt_reaper_timeout,
reaper);
bolt_info (LOG_TOPIC ("reaper"), "started");
}
gboolean
bolt_reaper_del_pid (BoltReaper *reaper,
guint pid)
{
gpointer p = GUINT_TO_POINTER (pid);
g_return_val_if_fail (BOLT_IS_REAPER (reaper), FALSE);
return g_hash_table_remove (reaper->pids, p);
}
gboolean
bolt_reaper_has_pid (BoltReaper *reaper,
guint pid)
{
gpointer p = GUINT_TO_POINTER (pid);
g_return_val_if_fail (BOLT_IS_REAPER (reaper), FALSE);
return g_hash_table_contains (reaper->pids, p);
}
bolt-0.9.2/boltd/bolt-reaper.h 0000664 0000000 0000000 00000002551 14174530510 0016156 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2020 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
#define BOLT_TYPE_REAPER bolt_reaper_get_type ()
G_DECLARE_FINAL_TYPE (BoltReaper, bolt_reaper, BOLT, REAPER, GObject);
BoltReaper * bolt_reaper_new (void);
void bolt_reaper_add_pid (BoltReaper *reaper,
guint pid,
const char *name);
gboolean bolt_reaper_del_pid (BoltReaper *reaper,
guint pid);
gboolean bolt_reaper_has_pid (BoltReaper *reaper,
guint pid);
G_END_DECLS
bolt-0.9.2/boltd/bolt-store.c 0000664 0000000 0000000 00000103734 14174530510 0016034 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-store.h"
#include "bolt-error.h"
#include "bolt-fs.h"
#include "bolt-io.h"
#include "bolt-log.h"
#include "bolt-str.h"
#include "bolt-time.h"
#include
/* ************************************ */
/* BoltStore */
static void bolt_store_initable_iface_init (GInitableIface *iface);
static gboolean bolt_store_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error);
static gboolean bolt_store_init_store (DIR *root,
GError **error);
struct _BoltStore
{
GObject object;
GFile *root;
GFile *domains;
GFile *devices;
GFile *keys;
GFile *times;
guint version;
};
enum {
PROP_STORE_0,
PROP_ROOT,
PROP_VERSION,
PROP_STORE_LAST
};
static GParamSpec *store_props[PROP_STORE_LAST] = { NULL, };
enum {
SIGNAL_DEVICE_ADDED,
SIGNAL_DEVICE_REMOVED,
SIGNAL_LAST
};
static guint signals[SIGNAL_LAST] = {0};
G_DEFINE_TYPE_WITH_CODE (BoltStore,
bolt_store,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
bolt_store_initable_iface_init));
static void
bolt_store_finalize (GObject *object)
{
BoltStore *store = BOLT_STORE (object);
g_clear_object (&store->root);
g_clear_object (&store->domains);
g_clear_object (&store->devices);
g_clear_object (&store->keys);
g_clear_object (&store->times);
G_OBJECT_CLASS (bolt_store_parent_class)->finalize (object);
}
static void
bolt_store_init (BoltStore *store)
{
}
static void
bolt_store_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltStore *store = BOLT_STORE (object);
switch (prop_id)
{
case PROP_ROOT:
g_value_set_object (value, store->root);
break;
case PROP_VERSION:
g_value_set_uint (value, store->version);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_store_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltStore *store = BOLT_STORE (object);
switch (prop_id)
{
case PROP_ROOT:
store->root = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_store_constructed (GObject *obj)
{
g_autofree char *path = NULL;
BoltStore *store = BOLT_STORE (obj);
G_OBJECT_CLASS (bolt_store_parent_class)->constructed (obj);
path = g_file_get_path (store->root);
bolt_info (LOG_TOPIC ("store"), "located at: %s", path);
store->devices = g_file_get_child (store->root, "devices");
store->domains = g_file_get_child (store->root, "domains");
store->keys = g_file_get_child (store->root, "keys");
store->times = g_file_get_child (store->root, "times");
}
static void
bolt_store_class_init (BoltStoreClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = bolt_store_finalize;
gobject_class->constructed = bolt_store_constructed;
gobject_class->get_property = bolt_store_get_property;
gobject_class->set_property = bolt_store_set_property;
store_props[PROP_ROOT] =
g_param_spec_object ("root",
NULL, NULL,
G_TYPE_FILE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME);
store_props[PROP_VERSION] =
g_param_spec_uint ("version",
NULL, NULL,
0, G_MAXUINT, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_STORE_LAST,
store_props);
signals[SIGNAL_DEVICE_ADDED] =
g_signal_new ("device-added",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
1, G_TYPE_STRING);
signals[SIGNAL_DEVICE_REMOVED] =
g_signal_new ("device-removed",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
1, G_TYPE_STRING);
}
static void
bolt_store_initable_iface_init (GInitableIface *iface)
{
iface->init = bolt_store_initialize;
}
static gboolean
bolt_store_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(DIR) root = NULL;
g_autofree char *path = NULL;
BoltStore *store = BOLT_STORE (initable);
gboolean ok;
path = g_file_get_path (store->root);
root = bolt_opendir (path, error);
if (root == NULL)
return FALSE;
ok = bolt_store_init_store (root, error);
if (!ok)
return FALSE;
ok = bolt_read_uint_at (dirfd (root),
"version",
&store->version,
&err);
if (!ok && !bolt_err_notfound (err))
return bolt_error_propagate (error, &err);
return TRUE;
}
/* internal methods */
#define DOMAIN_GROUP "domain"
#define DEVICE_GROUP "device"
#define USER_GROUP "user"
#define CFG_FILE "boltd.conf"
static gboolean
bolt_store_init_store (DIR *root,
GError **error)
{
gboolean empty;
gboolean ok;
/* initialize an empty store with the basic layout,
* which currently is just a 'version' field, since
* all other directories are created on-demand */
ok = bolt_dir_is_empty (root, &empty, error);
if (!ok)
return FALSE;
bolt_debug (LOG_TOPIC ("store"), "needs init: %s",
bolt_yesno (empty));
if (!empty)
return TRUE;
bolt_info (LOG_TOPIC ("store"), "initializing");
/* store is empty, create the 'version' file */
ok = bolt_write_uint_at (dirfd (root),
"version",
BOLT_STORE_VERSION,
error);
return ok;
}
/* public methods */
BoltStore *
bolt_store_new (const char *path, GError **error)
{
g_autoptr(GFile) root = NULL;
BoltStore *store;
root = g_file_new_for_path (path);
store = g_initable_new (BOLT_TYPE_STORE,
NULL, error,
"root", root,
NULL);
return store;
}
guint
bolt_store_get_version (BoltStore *store)
{
g_return_val_if_fail (BOLT_IS_STORE (store), 0);
return store->version;
}
GKeyFile *
bolt_store_config_load (BoltStore *store,
GError **error)
{
g_autoptr(GKeyFile) kf = NULL;
g_autoptr(GFile) sf = NULL;
g_autofree char *data = NULL;
gboolean ok;
gsize len;
g_return_val_if_fail (store != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
sf = g_file_get_child (store->root, CFG_FILE);
ok = g_file_load_contents (sf, NULL,
&data, &len,
NULL,
error);
if (!ok)
return NULL;
kf = g_key_file_new ();
ok = g_key_file_load_from_data (kf, data, len, G_KEY_FILE_NONE, error);
if (!ok)
return NULL;
return g_steal_pointer (&kf);
}
gboolean
bolt_store_config_save (BoltStore *store,
GKeyFile *config,
GError **error)
{
g_autoptr(GFile) sf = NULL;
g_autofree char *data = NULL;
gboolean ok;
gsize len;
g_return_val_if_fail (store != NULL, FALSE);
g_return_val_if_fail (config != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
sf = g_file_get_child (store->root, CFG_FILE);
data = g_key_file_to_data (config, &len, error);
if (!data)
return FALSE;
ok = g_file_replace_contents (sf,
data, len,
NULL, FALSE,
0,
NULL,
NULL, error);
return ok;
}
GStrv
bolt_store_list_uids (BoltStore *store,
const char *type,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(GDir) dir = NULL;
g_autoptr(GPtrArray) ids = NULL;
g_autofree char *path = NULL;
const char *name;
g_return_val_if_fail (BOLT_IS_STORE (store), NULL);
g_return_val_if_fail (type != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (bolt_streq (type, "devices"))
path = g_file_get_path (store->devices);
if (bolt_streq (type, "domains"))
path = g_file_get_path (store->domains);
if (path == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"unknown stored typed '%s'", type);
return NULL;
}
ids = g_ptr_array_new ();
dir = g_dir_open (path, 0, &err);
if (dir == NULL)
{
if (bolt_err_notfound (err))
return bolt_strv_from_ptr_array (&ids);
bolt_error_propagate (error, &err);
return NULL;
}
while ((name = g_dir_read_name (dir)) != NULL)
{
if (g_str_has_prefix (name, "."))
continue;
g_ptr_array_add (ids, g_strdup (name));
}
return bolt_strv_from_ptr_array (&ids);
}
gboolean
bolt_store_put_domain (BoltStore *store,
BoltDomain *domain,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(GFile) entry = NULL;
g_autoptr(GKeyFile) kf = NULL;
g_autofree char *path = NULL;
char * const * bootacl = NULL;
const char *uid;
gboolean ok;
gsize len;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
uid = bolt_domain_get_uid (domain);
g_assert (uid);
entry = g_file_get_child (store->domains, uid);
ok = bolt_fs_make_parent_dirs (entry, error);
if (!ok)
return FALSE;
kf = g_key_file_new ();
path = g_file_get_path (entry);
ok = g_key_file_load_from_file (kf,
path,
G_KEY_FILE_KEEP_COMMENTS,
&err);
if (!ok && !bolt_err_notfound (err))
{
bolt_warn_err (err, LOG_TOPIC ("store"),
"error loading existing domain");
g_clear_error (&err);
/* not fatal, keep going */
}
bootacl = bolt_domain_get_bootacl (domain);
len = bolt_strv_length (bootacl);
g_key_file_set_string_list (kf,
DOMAIN_GROUP,
"bootacl",
(const char * const *) bootacl,
len);
ok = g_key_file_save_to_file (kf, path, error);
if (!ok)
return FALSE;
g_object_set (G_OBJECT (domain),
"store", store,
NULL);
return ok;
}
BoltDomain *
bolt_store_get_domain (BoltStore *store,
const char *uid,
GError **error)
{
g_autoptr(GKeyFile) kf = NULL;
g_autoptr(GFile) db = NULL;
g_autoptr(GError) err = NULL;
g_auto(GStrv) bootacl = NULL;
g_autofree char *path = NULL;
BoltDomain *domain = NULL;
gboolean ok;
g_return_val_if_fail (store != NULL, NULL);
g_return_val_if_fail (uid != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
db = g_file_get_child (store->domains, uid);
path = g_file_get_path (db);
kf = g_key_file_new ();
ok = g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, error);
if (!ok)
return NULL;
bootacl = g_key_file_get_string_list (kf,
DOMAIN_GROUP,
"bootacl",
NULL,
&err);
if (bootacl == NULL && !bolt_err_notfound (err))
{
bolt_warn_err (err, LOG_DOM_UID (uid), LOG_TOPIC ("store"),
"failed to parse bootacl for domain '%s'",
uid);
g_clear_error (&err);
}
if (bolt_strv_isempty (bootacl))
g_clear_pointer (&bootacl, g_strfreev);
domain = g_object_new (BOLT_TYPE_DOMAIN,
"store", store,
"uid", uid,
"bootacl", bootacl,
NULL);
return domain;
}
gboolean
bolt_store_del_domain (BoltStore *store,
BoltDomain *domain,
GError **error)
{
g_autoptr(GFile) path = NULL;
const char *uid;
gboolean ok;
g_return_val_if_fail (store != NULL, FALSE);
g_return_val_if_fail (domain != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ok = bolt_domain_can_delete (domain, error);
if (!ok)
return FALSE;
uid = bolt_domain_get_uid (domain);
path = g_file_get_child (store->domains, uid);
ok = g_file_delete (path, NULL, error);
if (!ok)
return FALSE;
g_object_set (domain,
"store", NULL,
NULL);
return TRUE;
}
gboolean
bolt_store_put_device (BoltStore *store,
BoltDevice *device,
BoltPolicy policy,
BoltKey *key,
GError **error)
{
g_autoptr(GFile) entry = NULL;
g_autoptr(GKeyFile) kf = NULL;
g_autoptr(GError) err = NULL;
g_autofree char *data = NULL;
g_autofree char *path = NULL;
BoltDeviceType type;
const char *uid;
const char *label;
gboolean fresh;
gboolean ok;
guint64 ctime;
guint64 atime;
guint64 stime;
guint64 gen;
gsize len;
guint keystate;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (BOLT_IS_DEVICE (device), FALSE);
g_return_val_if_fail (key == NULL || BOLT_IS_KEY (key), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
uid = bolt_device_get_uid (device);
g_assert (uid);
entry = g_file_get_child (store->devices, uid);
ok = bolt_fs_make_parent_dirs (entry, error);
if (!ok)
return FALSE;
kf = g_key_file_new ();
path = g_file_get_path (entry);
ok = g_key_file_load_from_file (kf,
path,
G_KEY_FILE_KEEP_COMMENTS,
&err);
if (!ok)
{
if (!bolt_err_notfound (err))
bolt_warn_err (err, LOG_TOPIC ("store"), LOG_DEV_UID (uid),
"could not load previously stored device");
g_clear_error (&err);
}
g_key_file_set_string (kf, DEVICE_GROUP, "name", bolt_device_get_name (device));
g_key_file_set_string (kf, DEVICE_GROUP, "vendor", bolt_device_get_vendor (device));
gen = bolt_device_get_generation (device);
if (gen)
g_key_file_set_uint64 (kf, DEVICE_GROUP, "generation", gen);
type = bolt_device_get_device_type (device);
g_key_file_set_string (kf, DEVICE_GROUP, "type", bolt_device_type_to_string (type));
if (policy == BOLT_POLICY_DEFAULT)
policy = bolt_device_get_policy (device);
if (policy != BOLT_POLICY_DEFAULT)
{
const char *str = bolt_policy_to_string (policy);
g_key_file_set_string (kf, USER_GROUP, "policy", str);
}
label = bolt_device_get_label (device);
if (label != NULL)
g_key_file_set_string (kf, USER_GROUP, "label", label);
stime = bolt_device_get_storetime (device);
if (stime == 0)
stime = bolt_now_in_seconds ();
g_key_file_set_uint64 (kf, USER_GROUP, "storetime", stime);
data = g_key_file_to_data (kf, &len, error);
if (!data)
return FALSE;
keystate = bolt_device_get_keystate (device);
if (key)
{
ok = bolt_store_put_key (store, uid, key, &err);
if (!ok)
bolt_warn_err (err, "failed to store key");
else
keystate = bolt_key_get_state (key);
}
ok = g_file_replace_contents (entry,
data, len,
NULL, FALSE,
0,
NULL,
NULL, error);
if (!ok)
return FALSE;
fresh = bolt_device_get_stored (device) == FALSE;
g_object_set (device,
"store", store,
"policy", policy,
"key", keystate,
"storetime", stime,
NULL);
if (fresh)
g_signal_emit (store, signals[SIGNAL_DEVICE_ADDED], 0, uid);
ctime = bolt_device_get_conntime (device);
atime = bolt_device_get_authtime (device);
bolt_store_put_times (store, uid, NULL,
"conntime", ctime,
"authtime", atime,
NULL);
return TRUE;
}
BoltDevice *
bolt_store_get_device (BoltStore *store,
const char *uid,
GError **error)
{
g_autoptr(GKeyFile) kf = NULL;
g_autoptr(GFile) db = NULL;
g_autoptr(GError) err = NULL;
g_autofree char *name = NULL;
g_autofree char *vendor = NULL;
g_autofree char *data = NULL;
g_autofree char *typestr = NULL;
g_autofree char *polstr = NULL;
g_autofree char *label = NULL;
BoltDeviceType type;
BoltPolicy policy;
BoltKeyState key;
gboolean ok;
guint64 stime;
guint64 atime = 0;
guint64 ctime = 0;
guint gen;
gsize len;
g_return_val_if_fail (BOLT_IS_STORE (store), NULL);
g_return_val_if_fail (uid != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
db = g_file_get_child (store->devices, uid);
ok = g_file_load_contents (db, NULL,
&data, &len,
NULL,
error);
if (!ok)
return NULL;
kf = g_key_file_new ();
ok = g_key_file_load_from_data (kf, data, len, G_KEY_FILE_NONE, error);
if (!ok)
return NULL;
name = g_key_file_get_string (kf, DEVICE_GROUP, "name", NULL);
vendor = g_key_file_get_string (kf, DEVICE_GROUP, "vendor", NULL);
typestr = g_key_file_get_string (kf, DEVICE_GROUP, "type", NULL);
polstr = g_key_file_get_string (kf, USER_GROUP, "policy", NULL);
label = g_key_file_get_string (kf, USER_GROUP, "label", NULL);
gen = g_key_file_get_int64 (kf, DEVICE_GROUP, "generation", &err);
if (err != NULL && !bolt_err_notfound (err))
bolt_warn_err (err, LOG_TOPIC ("store"), "invalid generation");
g_clear_error (&err);
type = bolt_enum_from_string (BOLT_TYPE_DEVICE_TYPE, typestr, &err);
if (type == BOLT_DEVICE_UNKNOWN_TYPE)
{
bolt_warn_err (err, LOG_TOPIC ("store"), LOG_DEV_UID (uid),
"invalid device type");
g_clear_error (&err);
type = BOLT_DEVICE_PERIPHERAL;
}
policy = bolt_enum_from_string (BOLT_TYPE_POLICY, polstr, &err);
if (policy == BOLT_POLICY_UNKNOWN)
{
bolt_warn_err (err, LOG_TOPIC ("store"), LOG_DEV_UID (uid),
"invalid policy");
g_clear_error (&err);
policy = BOLT_POLICY_MANUAL;
}
if (label != NULL)
{
g_autofree char *tmp = g_steal_pointer (&label);
label = bolt_strdup_validate (tmp);
if (label == NULL)
bolt_warn (LOG_TOPIC ("store"), LOG_DEV_UID (uid),
"invalid device label: %s", label);
}
stime = g_key_file_get_uint64 (kf, USER_GROUP, "storetime", &err);
if (err != NULL && !bolt_err_notfound (err))
bolt_warn_err (err, LOG_TOPIC ("store"), "invalid enroll-time");
if (stime == 0)
{
g_autoptr(GFileInfo) info = NULL;
info = g_file_query_info (db,
"time::changed",
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
if (info != NULL)
stime = g_file_info_get_attribute_uint64 (info, "time::changed");
}
key = bolt_store_have_key (store, uid);
if (name == NULL || vendor == NULL)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"invalid device entry in store");
return NULL;
}
/* read timestamps, but failing is not fatal */
bolt_store_get_times (store, uid, NULL,
"conntime", &ctime,
"authtime", &atime,
NULL);
return g_object_new (BOLT_TYPE_DEVICE,
"uid", uid,
"name", name,
"vendor", vendor,
"generation", gen,
"type", type,
"status", BOLT_STATUS_DISCONNECTED,
"store", store,
"policy", policy,
"key", key,
"storetime", stime,
"conntime", ctime,
"authtime", atime,
"label", label,
NULL);
}
gboolean
bolt_store_del_device (BoltStore *store,
const char *uid,
GError **error)
{
g_autoptr(GFile) devpath = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
devpath = g_file_get_child (store->devices, uid);
ok = g_file_delete (devpath, NULL, error);
if (ok)
g_signal_emit (store, signals[SIGNAL_DEVICE_REMOVED], 0, uid);
return ok;
}
gboolean
bolt_store_get_time (BoltStore *store,
const char *uid,
const char *timesel,
guint64 *outval,
GError **error)
{
g_autoptr(GFile) gf = NULL;
g_autoptr(GFileInfo) info = NULL;
g_autofree char *fn = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (timesel != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
fn = g_strdup_printf ("%s.%s", uid, timesel);
gf = g_file_get_child (store->times, fn);
info = g_file_query_info (gf, "time::modified",
G_FILE_QUERY_INFO_NONE,
NULL, error);
ok = info != NULL;
if (ok && outval != NULL)
*outval = g_file_info_get_attribute_uint64 (info, "time::modified");
return ok;
}
gboolean
bolt_store_get_times (BoltStore *store,
const char *uid,
GError **error,
...)
{
gboolean res = TRUE;
gboolean ok = TRUE;
const char *ts;
va_list args;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
va_start (args, error);
while ((ts = va_arg (args, const char *)) != NULL)
{
g_autoptr(GError) err = NULL;
guint64 *val = va_arg (args, guint64 *);
*val = 0;
ok = bolt_store_get_time (store, uid, ts, val, &err);
if (!ok && !bolt_err_notfound (err))
{
if (error != NULL)
{
res = bolt_error_propagate (error, &err);
break;
}
/* error variable NULL, caller doesn't care, we keep going */
bolt_warn_err (err, LOG_DEV_UID (uid), LOG_TOPIC ("store"),
"failed to read timestamp '%s'", ts);
}
}
va_end (args);
return res;
}
gboolean
bolt_store_put_time (BoltStore *store,
const char *uid,
const char *timesel,
guint64 val,
GError **error)
{
g_autoptr(GFile) gf = NULL;
g_autofree char *fn = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (timesel != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
fn = g_strdup_printf ("%s.%s", uid, timesel);
gf = g_file_get_child (store->times, fn);
ok = bolt_fs_make_parent_dirs (gf, error);
if (!ok)
return FALSE;
ok = bolt_fs_touch (gf, val, val, error);
return ok;
}
gboolean
bolt_store_put_times (BoltStore *store,
const char *uid,
GError **error,
...)
{
gboolean res = TRUE;
gboolean ok = TRUE;
const char *ts;
va_list args;
if (store == NULL)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"device '%s' is not stored", uid);
return FALSE;
}
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
va_start (args, error);
while ((ts = va_arg (args, const char *)) != NULL)
{
g_autoptr(GError) err = NULL;
guint64 val = va_arg (args, guint64);
if (val == 0)
continue;
ok = bolt_store_put_time (store,
uid,
ts,
val,
&err);
if (!ok)
{
if (error != NULL)
{
res = bolt_error_propagate (error, &err);
break;
}
/* error variable NULL, caller doesn't care, we keep going */
bolt_warn_err (err, LOG_DEV_UID (uid), LOG_TOPIC ("store"),
"failed to update timestamp '%s'", ts);
}
}
va_end (args);
return res;
}
gboolean
bolt_store_del_time (BoltStore *store,
const char *uid,
const char *timesel,
GError **error)
{
g_autoptr(GFile) pathfile = NULL;
g_autofree char *name = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (timesel != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
name = g_strdup_printf ("%s.%s", uid, timesel);
pathfile = g_file_get_child (store->times, name);
ok = g_file_delete (pathfile, NULL, error);
return ok;
}
gboolean
bolt_store_del_times (BoltStore *store,
const char *uid,
GError **error,
...)
{
gboolean res = TRUE;
gboolean ok = TRUE;
const char *ts;
va_list args;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
va_start (args, error);
while ((ts = va_arg (args, const char *)) != NULL)
{
g_autoptr(GError) err = NULL;
ok = bolt_store_del_time (store, uid, ts, &err);
if (!ok && !bolt_err_notfound (err))
{
if (error != NULL)
{
res = bolt_error_propagate (error, &err);
break;
}
/* error variable NULL, caller doesn't care, we keep going */
bolt_warn_err (err, LOG_DEV_UID (uid), LOG_TOPIC ("store"),
"failed to delete timestamp '%s'", ts);
}
}
va_end (args);
return res;
}
gboolean
bolt_store_put_key (BoltStore *store,
const char *uid,
BoltKey *key,
GError **error)
{
g_autoptr(GFile) keypath = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (BOLT_IS_KEY (key), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
keypath = g_file_get_child (store->keys, uid);
ok = bolt_fs_make_parent_dirs (keypath, error);
if (ok)
ok = bolt_key_save_file (key, keypath, error);
return ok;
}
BoltKeyState
bolt_store_have_key (BoltStore *store,
const char *uid)
{
g_autoptr(GFileInfo) keyinfo = NULL;
g_autoptr(GFile) keypath = NULL;
g_autoptr(GError) err = NULL;
guint key = BOLT_KEY_MISSING;
g_return_val_if_fail (BOLT_IS_STORE (store), key);
g_return_val_if_fail (uid != NULL, key);
keypath = g_file_get_child (store->keys, uid);
keyinfo = g_file_query_info (keypath, "standard::*", 0, NULL, &err);
if (keyinfo != NULL)
key = BOLT_KEY_HAVE; /* todo: check size */
else if (!bolt_err_notfound (err))
bolt_warn_err (err, LOG_DEV_UID (uid), "error querying key info");
return key;
}
BoltKey *
bolt_store_get_key (BoltStore *store,
const char *uid,
GError **error)
{
g_autoptr(GFile) keypath = NULL;
g_return_val_if_fail (BOLT_IS_STORE (store), NULL);
g_return_val_if_fail (uid != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
keypath = g_file_get_child (store->keys, uid);
return bolt_key_load_file (keypath, error);
}
gboolean
bolt_store_del_key (BoltStore *store,
const char *uid,
GError **error)
{
g_autoptr(GFile) keypath = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
keypath = g_file_get_child (store->keys, uid);
ok = g_file_delete (keypath, NULL, error);
return ok;
}
gboolean
bolt_store_del (BoltStore *store,
BoltDevice *dev,
GError **error)
{
g_autoptr(GError) err = NULL;
const char *uid;
gboolean ok;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
uid = bolt_device_get_uid (dev);
ok = bolt_store_del_key (store, uid, &err);
if (!ok && !bolt_err_notfound (err))
{
g_propagate_prefixed_error (error,
g_steal_pointer (&err),
"could not delete key: ");
return FALSE;
}
ok = bolt_store_del_device (store, uid, error);
if (!ok)
return FALSE;
bolt_store_del_times (store, uid, NULL,
"conntime", "authtime",
NULL);
g_object_set (dev,
"store", NULL,
"key", BOLT_KEY_MISSING,
"policy", BOLT_POLICY_DEFAULT,
NULL);
return ok;
}
BoltJournal *
bolt_store_open_journal (BoltStore *store,
const char *type,
const char *name,
GError **error)
{
g_autoptr(GFile) root = NULL;
BoltJournal *journal;
g_return_val_if_fail (store != NULL, NULL);
g_return_val_if_fail (type != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
root = g_file_get_child (store->root, type);
journal = bolt_journal_new (root, name, error);
return journal;
}
gboolean
bolt_store_del_journal (BoltStore *store,
const char *type,
const char *name,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(GFile) root = NULL;
g_autoptr(GFile) journal = NULL;
gboolean ok;
g_return_val_if_fail (store != NULL, FALSE);
g_return_val_if_fail (type != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
root = g_file_get_child (store->root, type);
journal = g_file_get_child (root, name);
ok = g_file_delete (journal, NULL, &err);
if (!ok && !bolt_err_notfound (err))
{
bolt_error_propagate (error, &err);
return FALSE;
}
return TRUE;
}
gboolean
bolt_store_has_journal (BoltStore *store,
const char *type,
const char *name)
{
g_autoptr(GFile) root = NULL;
g_autoptr(GFile) journal = NULL;
g_return_val_if_fail (store != NULL, FALSE);
g_return_val_if_fail (type != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
root = g_file_get_child (store->root, type);
journal = g_file_get_child (root, name);
return g_file_query_exists (journal, NULL);
}
gboolean
bolt_store_upgrade (BoltStore *store,
gboolean *upgrade,
GError **error)
{
g_autoptr(DIR) root = NULL;
g_autofree char *path = NULL;
gboolean need_upgrade;
gboolean ok;
g_return_val_if_fail (BOLT_IS_STORE (store), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
need_upgrade = store->version != BOLT_STORE_VERSION;
if (upgrade)
*upgrade = need_upgrade;
if (!need_upgrade)
return TRUE;
path = g_file_get_path (store->root);
root = bolt_opendir (path, error);
if (!root)
return FALSE;
ok = bolt_write_int_at (dirfd (root),
".version-upgrade",
BOLT_STORE_VERSION,
error);
if (!ok)
return FALSE;
ok = bolt_renameat (dirfd (root),
".version-upgrade",
dirfd (root),
"version",
error);
if (!ok)
{
bolt_unlink_at (dirfd (root), ".version-upgrade", 0, NULL);
return FALSE;
}
store->version = BOLT_STORE_VERSION;
g_object_notify_by_pspec (G_OBJECT (store),
store_props[PROP_VERSION]);
return ok;
}
bolt-0.9.2/boltd/bolt-store.h 0000664 0000000 0000000 00000014215 14174530510 0016034 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include "bolt-device.h"
#include "bolt-domain.h"
#include "bolt-enums.h"
#include "bolt-key.h"
#include "bolt-journal.h"
G_BEGIN_DECLS
#define BOLT_STORE_VERSION 1
/* BoltStore - database for devices, keys */
#define BOLT_TYPE_STORE bolt_store_get_type ()
G_DECLARE_FINAL_TYPE (BoltStore, bolt_store, BOLT, STORE, GObject);
BoltStore * bolt_store_new (const char *path,
GError **error);
guint bolt_store_get_version (BoltStore *store);
GKeyFile * bolt_store_config_load (BoltStore *store,
GError **error);
gboolean bolt_store_config_save (BoltStore *store,
GKeyFile *config,
GError **error);
GStrv bolt_store_list_uids (BoltStore *store,
const char *type,
GError **error);
gboolean bolt_store_put_domain (BoltStore *store,
BoltDomain *domain,
GError **error);
BoltDomain * bolt_store_get_domain (BoltStore *store,
const char *uid,
GError **error);
gboolean bolt_store_del_domain (BoltStore *store,
BoltDomain *domain,
GError **error);
gboolean bolt_store_del (BoltStore *store,
BoltDevice *dev,
GError **error);
gboolean bolt_store_put_device (BoltStore *store,
BoltDevice *device,
BoltPolicy policy,
BoltKey *key,
GError **error);
BoltDevice * bolt_store_get_device (BoltStore *store,
const char *uid,
GError **error);
gboolean bolt_store_del_device (BoltStore *store,
const char *uid,
GError **error);
gboolean bolt_store_put_time (BoltStore *store,
const char *uid,
const char *timesel,
guint64 val,
GError **error);
gboolean bolt_store_put_times (BoltStore *store,
const char *uid,
GError **error,
...) G_GNUC_NULL_TERMINATED;
gboolean bolt_store_get_time (BoltStore *store,
const char *uid,
const char *timesel,
guint64 *outval,
GError **error);
gboolean bolt_store_get_times (BoltStore *store,
const char *uid,
GError **error,
...) G_GNUC_NULL_TERMINATED;
gboolean bolt_store_del_time (BoltStore *store,
const char *uid,
const char *timesel,
GError **error);
gboolean bolt_store_del_times (BoltStore *store,
const char *uid,
GError **error,
...) G_GNUC_NULL_TERMINATED;
gboolean bolt_store_put_key (BoltStore *store,
const char *uid,
BoltKey *key,
GError **error);
BoltKeyState bolt_store_have_key (BoltStore *store,
const char *uid);
BoltKey * bolt_store_get_key (BoltStore *store,
const char *uid,
GError **error);
gboolean bolt_store_del_key (BoltStore *store,
const char *uid,
GError **error);
BoltJournal * bolt_store_open_journal (BoltStore *store,
const char *type,
const char *name,
GError **error);
gboolean bolt_store_del_journal (BoltStore *store,
const char *type,
const char *name,
GError **error);
gboolean bolt_store_has_journal (BoltStore *store,
const char *type,
const char *name);
/* store upgrades */
gboolean bolt_store_upgrade (BoltStore *store,
gboolean *upgrade,
GError **error);
G_END_DECLS
bolt-0.9.2/boltd/bolt-sysfs.c 0000664 0000000 0000000 00000037534 14174530510 0016053 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-sysfs.h"
#include "bolt-error.h"
#include "bolt-io.h"
#include "bolt-log.h"
#include "bolt-names.h"
#include "bolt-str.h"
#include
#include
#include
#include
typedef struct udev_device udev_device;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (udev_device, udev_device_unref);
void
bolt_ident_clear (BoltIdent *id)
{
if (id->udev == NULL)
return;
udev_device_unref (id->udev);
memset (id, 0, sizeof (BoltIdent));
}
static const char *
sysfs_get_sysattr_value (struct udev_device *dev,
const char *attr,
GError **error)
{
const char *val;
g_return_val_if_fail (dev != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
val = udev_device_get_sysattr_value (dev, attr);
if (val == NULL)
{
int code = errno;
const char *path = udev_device_get_syspath (dev);
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"could not get '%s' for %s: %s",
attr, path, g_strerror (code));
}
return val;
}
const char *
bolt_sysfs_device_get_unique_id (struct udev_device *dev,
GError **error)
{
g_return_val_if_fail (dev != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return sysfs_get_sysattr_value (dev, BOLT_SYSFS_UNIQUE_ID, error);
}
gint64
bolt_sysfs_device_get_time (struct udev_device *udev,
BoltStatTime st)
{
const char *path;
struct stat sb;
gint64 ms = 0;
int r;
path = udev_device_get_syspath (udev);
if (path == NULL)
return 0;
r = lstat (path, &sb);
if (r == -1)
return 0;
switch (st)
{
case BOLT_ST_CTIME:
ms = (gint64) sb.st_ctim.tv_sec;
break;
case BOLT_ST_ATIME:
ms = (gint64) sb.st_atim.tv_sec;
break;
case BOLT_ST_MTIME:
ms = (gint64) sb.st_mtim.tv_sec;
break;
}
if (ms < 0)
ms = 0;
return ms;
}
gboolean
bolt_sysfs_device_is_domain (struct udev_device *udev,
GError **error)
{
const char *devtype = udev_device_get_devtype (udev);
const char *subsystem = udev_device_get_subsystem (udev);
gboolean is_domain;
is_domain = bolt_streq (subsystem, "thunderbolt") &&
bolt_streq (devtype, "thunderbolt_domain");
if (!is_domain)
{
const char *syspath = udev_device_get_syspath (udev);
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"device '%s' is not a thunderbolt domain",
syspath);
}
return is_domain;
}
struct udev_device *
bolt_sysfs_domain_for_device (struct udev_device *udev,
struct udev_device **host_out)
{
struct udev_device *parent;
struct udev_device *host;
gboolean found;
found = FALSE;
parent = udev;
do
{
host = parent;
parent = udev_device_get_parent (host);
if (!parent)
break;
found = bolt_sysfs_device_is_domain (parent, NULL);
}
while (!found);
if (!found)
return NULL;
if (host_out)
*host_out = host;
return parent;
}
BoltSecurity
bolt_sysfs_security_for_device (struct udev_device *udev,
GError **error)
{
struct udev_device *parent = NULL;
const char *v;
BoltSecurity s;
if (bolt_sysfs_device_is_domain (udev, NULL))
parent = udev;
else
parent = bolt_sysfs_domain_for_device (udev, NULL);
if (parent == NULL)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"failed to determine domain device");
return BOLT_SECURITY_UNKNOWN;
}
v = udev_device_get_sysattr_value (parent, "security");
s = bolt_enum_from_string (BOLT_TYPE_SECURITY, v, error);
return s;
}
static const char *
read_sysattr_name (struct udev_device *udev,
const char *attr,
GError **error)
{
g_autofree char *s = NULL;
const char *v;
s = g_strdup_printf ("%s_name", attr);
v = udev_device_get_sysattr_value (udev, s);
if (v != NULL)
return v;
return sysfs_get_sysattr_value (udev, attr, error);
}
gboolean
bolt_sysfs_device_ident (struct udev_device *udev,
BoltIdent *id,
GError **error)
{
const char *name;
const char *vendor;
g_return_val_if_fail (udev != NULL, FALSE);
g_return_val_if_fail (id != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
vendor = read_sysattr_name (udev, "vendor", error);
if (vendor == NULL)
return FALSE;
name = read_sysattr_name (udev, "device", error);
if (name == NULL)
return FALSE;
id->udev = udev_device_ref (udev);
id->name = name;
id->vendor = vendor;
return TRUE;
}
gboolean
bolt_sysfs_host_ident (struct udev_device *dev,
BoltIdent *id,
GError **error)
{
g_autoptr(udev_device) dmi = NULL;
struct udev *udev;
const char *name;
const char *vendor;
const char *attr;
gboolean ok;
g_return_val_if_fail (dev != NULL, FALSE);
g_return_val_if_fail (id != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* First check if the host controller has normal device ident,
* which should be present for all controller that have a DROM. */
ok = bolt_sysfs_device_ident (dev, id, NULL);
if (ok)
return TRUE;
/* On embedded thunderbolt controllers without DROM, we
* fall back to using the SMBIOS/DMI system information */
udev = udev_device_get_udev (dev);
dmi = udev_device_new_from_syspath (udev, BOLT_SYSFS_DMI_ID);
if (dmi == NULL)
{
int code = errno;
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"could not open dmi/id device: %s",
g_strerror (code));
return FALSE;
}
attr = BOLT_SYSFS_DMI_SYS_VENDOR;
vendor = sysfs_get_sysattr_value (dmi, attr, error);
if (vendor == NULL)
return FALSE;
/* Almost all systems are using the product_name attribute
* for the human readable string, with exception of Lenovo,
* that instead uses the product_version, so we special
* case that. */
attr = BOLT_SYSFS_DMI_PRODUCT_NAME;
if (!g_ascii_strcasecmp (vendor, "lenovo"))
{
attr = BOLT_SYSFS_DMI_PRODUCT_VERSION;
vendor = "Lenovo";
}
name = sysfs_get_sysattr_value (dmi, attr, error);
if (name == NULL)
return FALSE;
id->udev = g_steal_pointer (&dmi);
id->name = name;
id->vendor = vendor;
return TRUE;
}
static int
bolt_syfs_count_tb_devices (struct udev *udev,
struct udev_device *parent,
GError **error)
{
struct udev_enumerate *e;
struct udev_list_entry *l, *devices;
int r, count = 0;
e = udev_enumerate_new (udev);
udev_enumerate_add_match_subsystem (e, "thunderbolt");
udev_enumerate_add_match_property (e, "DEVTYPE", "thunderbolt_device");
if (parent)
udev_enumerate_add_match_parent (e, parent);
r = udev_enumerate_scan_devices (e);
if (r < 0)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"failed to scan udev: %s",
g_strerror (-r));
return r;
}
devices = udev_enumerate_get_list_entry (e);
udev_list_entry_foreach (l, devices)
count++;
udev_enumerate_unref (e);
return count;
}
int
bolt_sysfs_count_hosts (struct udev *udev,
GError **error)
{
struct udev_enumerate *e;
struct udev_list_entry *l, *devices;
int r, count = 0;
e = udev_enumerate_new (udev);
udev_enumerate_add_match_subsystem (e, "thunderbolt");
udev_enumerate_add_match_property (e, "DEVTYPE", "thunderbolt_domain");
r = udev_enumerate_scan_devices (e);
if (r < 0)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"failed to scan udev: %s",
g_strerror (-r));
return r;
}
devices = udev_enumerate_get_list_entry (e);
udev_list_entry_foreach (l, devices)
{
struct udev_device *udevice = NULL;
const char *syspath;
int n;
syspath = udev_list_entry_get_name (l);
udevice = udev_device_new_from_syspath (udev, syspath);
if (udevice == NULL)
continue;
n = bolt_syfs_count_tb_devices (udev, udevice, NULL);
if (n > 0)
count++;
udev_device_unref (udevice);
}
udev_enumerate_unref (e);
return count;
}
gboolean
bolt_sysfs_nhi_id_for_domain (struct udev_device *udev,
guint32 *id,
GError **error)
{
struct udev_device *parent;
const char *str;
gboolean ok;
ok = bolt_sysfs_device_is_domain (udev, error);
if (!ok)
return FALSE;
parent = udev_device_get_parent (udev);
if (parent == NULL)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"failed to get parent for domain: %s",
g_strerror (errno));
return FALSE;
}
str = udev_device_get_sysattr_value (parent, "device");
if (str == NULL)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"failed to get PCI id for NHI device: %s",
g_strerror (errno));
return FALSE;
}
return bolt_str_parse_as_uint32 (str, id, error);
}
static gint
sysfs_get_sysattr_value_as_int (struct udev_device *udev,
const char *attr)
{
const char *str;
gboolean ok;
gint val;
g_return_val_if_fail (udev != NULL, FALSE);
str = udev_device_get_sysattr_value (udev, attr);
if (str == NULL)
return -errno;
ok = bolt_str_parse_as_int (str, &val, NULL);
if (!ok)
return -errno;
return val;
}
static gssize
sysfs_get_sysattr_size (struct udev_device *udev,
const char *attr)
{
const char *str;
g_return_val_if_fail (udev != NULL, FALSE);
str = udev_device_get_sysattr_value (udev, attr);
if (str == NULL)
return -errno;
return strlen (str);
}
void
bolt_sysfs_read_link_speed (struct udev_device *udev,
BoltLinkSpeed *speed)
{
struct
{
const char *name;
guint32 *item;
} entries[] = {
{BOLT_SYSFS_RX_LANES, &speed->rx.lanes},
{BOLT_SYSFS_RX_SPEED, &speed->rx.speed},
{BOLT_SYSFS_TX_LANES, &speed->tx.lanes},
{BOLT_SYSFS_TX_SPEED, &speed->tx.speed},
};
for (unsigned i = 0; i < G_N_ELEMENTS (entries); i++)
{
const char *name = entries[i].name;
int res = sysfs_get_sysattr_value_as_int (udev, name);
*entries[i].item = res > 0 ? res : 0;
}
}
gboolean
bolt_sysfs_info_for_device (struct udev_device *udev,
gboolean full,
BoltDevInfo *info,
GError **error)
{
struct udev_device *parent;
int auth;
int gen;
g_return_val_if_fail (udev != NULL, FALSE);
g_return_val_if_fail (info != NULL, FALSE);
info->keysize = -1;
info->ctim = -1;
info->full = FALSE;
info->parent = NULL;
info->generation = 0;
info->syspath = NULL;
auth = sysfs_get_sysattr_value_as_int (udev, "authorized");
info->authorized = auth;
if (auth < 0)
{
int code = g_io_error_from_errno (errno);
g_set_error (error, G_IO_ERROR, code,
"could not read 'authorized': %s",
g_strerror (errno));
return FALSE;
}
info->keysize = sysfs_get_sysattr_size (udev, "key");
info->boot = sysfs_get_sysattr_value_as_int (udev, "boot");
if (full == FALSE)
return TRUE;
info->full = TRUE;
info->ctim = bolt_sysfs_device_get_time (udev, BOLT_ST_CTIME);
info->syspath = udev_device_get_syspath (udev);
parent = udev_device_get_parent (udev);
if (parent != NULL)
info->parent = udev_device_get_sysattr_value (parent, "unique_id");
gen = sysfs_get_sysattr_value_as_int (udev, BOLT_SYSFS_GENERATION);
if (gen > 0)
info->generation = gen;
bolt_sysfs_read_link_speed (udev, &info->linkspeed);
return TRUE;
}
gboolean
bolt_sysfs_read_boot_acl (struct udev_device *udev,
GStrv *out,
GError **error)
{
g_auto(GStrv) acl = NULL;
const char *val;
g_return_val_if_fail (udev != NULL, FALSE);
g_return_val_if_fail (out != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
val = udev_device_get_sysattr_value (udev, "boot_acl");
if (val)
acl = g_strsplit (val, ",", 1024);
else if (errno != ENOENT)
return bolt_error_for_errno (error, errno, "%m");
/* if the attribute exists but is empty, return NULL */
if (!bolt_strv_isempty (acl))
*out = g_steal_pointer (&acl);
else
*out = NULL;
return TRUE;
}
gboolean
bolt_sysfs_write_boot_acl (const char *device,
GStrv acl,
GError **error)
{
g_autofree char *val = NULL;
g_autofree char *path = NULL;
g_return_val_if_fail (device != NULL, FALSE);
g_return_val_if_fail (acl != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
val = g_strjoinv (",", acl);
path = g_build_filename (device, "boot_acl", NULL);
return bolt_file_write_all (path, val, -1, error);
}
gboolean
bolt_sysfs_read_iommu (struct udev_device *udev,
gboolean *out,
GError **error)
{
int val = 0;
g_return_val_if_fail (udev != NULL, FALSE);
g_return_val_if_fail (out != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
val = sysfs_get_sysattr_value_as_int (udev, BOLT_SYSFS_IOMMU);
if (val < 0 && val != -ENOENT)
return bolt_error_for_errno (error, errno, "failed to read %s: %s",
BOLT_SYSFS_IOMMU, g_strerror (-val));
*out = val > 0;
return TRUE;
}
/* NHI PCI id related */
static struct
{
guint32 pci_id;
gboolean stable; /* Does the UUID change on reboot */
} nhi_table[] = {
{0x157d, TRUE}, // WIN_RIDGE_2C_NHI
{0x15bf, TRUE}, // ALPINE_RIDGE_LP_NHI
{0x15d2, TRUE}, // ALPINE_RIDGE_C_4C_NHI
{0x15d9, TRUE}, // ALPINE_RIDGE_C_2C_NHI
{0x15dc, TRUE}, // ALPINE_RIDGE_LP_USBONLY_NHI
{0x15dd, TRUE}, // ALPINE_RIDGE_USBONLY_NH
{0x15de, TRUE}, // ALPINE_RIDGE_C_USBONLY_NHI
{0x15e8, TRUE}, // TITAN_RIDGE_2C_NHI
{0x15eb, TRUE}, // TITAN_RIDGE_4C_NHI
{0x8a0d, FALSE}, // ICL_NHI1
{0x8a17, FALSE}, // ICL_NHI0
{0x9a1b, FALSE}, // TGL_NHI0
{0x9a1d, FALSE}, // TGL_NHI1
};
gboolean
bolt_nhi_uuid_is_stable (guint32 pci_id,
gboolean *stability,
GError **error)
{
g_return_val_if_fail (stability != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
for (gsize i = 0; i < G_N_ELEMENTS (nhi_table); i++)
{
if (pci_id == nhi_table[i].pci_id)
{
*stability = nhi_table[i].stable;
return TRUE;
}
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"unknown NHI PCI id '0x%04x'", pci_id);
return FALSE;
}
bolt-0.9.2/boltd/bolt-sysfs.h 0000664 0000000 0000000 00000011021 14174530510 0016037 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-wire.h"
#include
struct udev;
struct udev_device;
G_BEGIN_DECLS
/* Device identification */
typedef struct _BoltIdent BoltIdent;
struct _BoltIdent
{
struct udev_device *udev;
const char *name;
const char *vendor;
};
#define BOLT_IDENT_INIT {NULL, NULL, NULL}
void bolt_ident_clear (BoltIdent *id);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (BoltIdent, bolt_ident_clear);
const char * bolt_sysfs_device_get_unique_id (struct udev_device *dev,
GError **error);
typedef enum BoltStatTime {
BOLT_ST_ATIME,
BOLT_ST_CTIME,
BOLT_ST_MTIME
} BoltStatTime;
gint64 bolt_sysfs_device_get_time (struct udev_device *udev,
BoltStatTime st);
gboolean bolt_sysfs_device_is_domain (struct udev_device *udev,
GError **error);
struct udev_device * bolt_sysfs_domain_for_device (struct udev_device *udev,
struct udev_device **host);
BoltSecurity bolt_sysfs_security_for_device (struct udev_device *udev,
GError **error);
gboolean bolt_sysfs_device_ident (struct udev_device *udev,
BoltIdent *id,
GError **error);
gboolean bolt_sysfs_host_ident (struct udev_device *udev,
BoltIdent *id,
GError **error);
int bolt_sysfs_count_hosts (struct udev *udev,
GError **error);
gboolean bolt_sysfs_nhi_id_for_domain (struct udev_device *udev,
guint32 *id,
GError **error);
typedef struct _BoltDevInfo
{
/* always included */
gint authorized;
gssize keysize;
gint boot;
/* if 'full' is true the rest is valid */
gboolean full;
gint64 ctim;
const char *syspath;
const char *parent; /* the uid */
guint generation;
/* link speed, may be 0 if unknown */
BoltLinkSpeed linkspeed;
} BoltDevInfo;
gboolean bolt_sysfs_info_for_device (struct udev_device *udev,
gboolean full,
BoltDevInfo *info,
GError **error);
void bolt_sysfs_read_link_speed (struct udev_device *udev,
BoltLinkSpeed *speed);
gboolean bolt_sysfs_read_boot_acl (struct udev_device *udev,
GStrv *out,
GError **error);
gboolean bolt_sysfs_write_boot_acl (const char *device,
GStrv acl,
GError **error);
gboolean bolt_sysfs_read_iommu (struct udev_device *udev,
gboolean *out,
GError **error);
/* NHI id related functions*/
gboolean bolt_nhi_uuid_is_stable (guint32 pci_id,
gboolean *stability,
GError **error);
G_END_DECLS
bolt-0.9.2/boltd/bolt-udev.c 0000664 0000000 0000000 00000026450 14174530510 0015642 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-udev.h"
#include "bolt-error.h"
#include "bolt-sysfs.h"
#include
#include
#include
typedef struct udev_monitor udev_monitor;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (udev_monitor, udev_monitor_unref);
typedef struct udev_device udev_device;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (udev_device, udev_device_unref);
static void udev_initable_iface_init (GInitableIface *iface);
static gboolean bolt_udev_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error);
/* */
struct _BoltUdev
{
GObject object;
/* the native udev things */
struct udev *udev;
struct udev_monitor *monitor;
GSource *source;
/* properties */
char *name;
GStrv filter;
};
enum {
PROP_0,
PROP_NAME,
PROP_FILTER,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = { NULL, };
enum {
SIGNAL_UEVENT,
SIGNAL_LAST,
};
static guint signals[SIGNAL_LAST] = { 0, };
G_DEFINE_TYPE_WITH_CODE (BoltUdev,
bolt_udev,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
udev_initable_iface_init));
static void
bolt_udev_finalize (GObject *object)
{
BoltUdev *udev = BOLT_UDEV (object);
if (udev->monitor)
{
udev_monitor_unref (udev->monitor);
udev->monitor = NULL;
g_source_destroy (udev->source);
g_source_unref (udev->source);
udev->source = NULL;
}
g_clear_pointer (&udev->udev, udev_unref);
g_clear_pointer (&udev->name, g_free);
g_clear_pointer (&udev->filter, g_strfreev);
G_OBJECT_CLASS (bolt_udev_parent_class)->finalize (object);
}
static void
bolt_udev_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltUdev *udev = BOLT_UDEV (object);
switch (prop_id)
{
case PROP_NAME:
g_value_set_string (value, udev->name);
break;
case PROP_FILTER:
g_value_set_boxed (value, udev->filter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_udev_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltUdev *udev = BOLT_UDEV (object);
switch (prop_id)
{
case PROP_NAME:
udev->name = g_value_dup_string (value);
break;
case PROP_FILTER:
udev->filter = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_udev_init (BoltUdev *udev)
{
}
static void
bolt_udev_class_init (BoltUdevClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = bolt_udev_get_property;
gobject_class->set_property = bolt_udev_set_property;
gobject_class->finalize = bolt_udev_finalize;
props[PROP_NAME] =
g_param_spec_string ("name",
NULL, NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
props[PROP_FILTER] =
g_param_spec_boxed ("filter",
NULL, NULL,
G_TYPE_STRV,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
signals[SIGNAL_UEVENT] =
g_signal_new ("uevent",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE,
2,
G_TYPE_STRING,
G_TYPE_POINTER);
}
static void
udev_initable_iface_init (GInitableIface *iface)
{
iface->init = bolt_udev_initialize;
}
/* internal methods */
static gboolean
monitor_add_filter (struct udev_monitor *monitor,
const char *subsystem_devtype,
GError **error)
{
g_autofree char *subsystem = NULL;
char *devtype = NULL;
gboolean ok;
int r;
subsystem = g_strdup (subsystem_devtype);
devtype = strchr (subsystem, '/');
if (devtype != NULL)
*devtype++ = '\0';
r = udev_monitor_filter_add_match_subsystem_devtype (monitor,
subsystem,
devtype);
ok = r > -1;
if (!ok)
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"udev: could not add match for '%s' (%s) to monitor",
subsystem, devtype ? : "*");
return ok;
}
static gboolean
setup_monitor (BoltUdev *udev,
const char *name,
const GStrv filter,
GSourceFunc callback,
udev_monitor **monitor_out,
GSource **watch_out,
GError **error)
{
g_autoptr(udev_monitor) monitor = NULL;
g_autoptr(GIOChannel) channel = NULL;
GSource *watch;
gboolean ok;
int fd;
int res;
monitor = udev_monitor_new_from_netlink (udev->udev, name);
if (monitor == NULL)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"udev: could not create monitor");
return FALSE;
}
udev_monitor_set_receive_buffer_size (monitor, 128 * 1024 * 1024);
for (guint i = 0; filter && filter[i] != NULL; i++)
{
ok = monitor_add_filter (monitor, filter[i], error);
if (!ok)
return FALSE;
}
res = udev_monitor_enable_receiving (monitor);
if (res < 0)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"udev: could not enable monitoring");
return FALSE;
}
fd = udev_monitor_get_fd (monitor);
if (fd < 0)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"udev: could not obtain fd for monitoring");
return FALSE;
}
channel = g_io_channel_unix_new (fd);
watch = g_io_create_watch (channel, G_IO_IN);
g_source_set_callback (watch, callback, udev, NULL);
g_source_attach (watch, g_main_context_get_thread_default ());
*monitor_out = udev_monitor_ref (monitor);
*watch_out = watch;
return TRUE;
}
static gboolean
handle_uevent_udev (GIOChannel *source,
GIOCondition condition,
gpointer user_data)
{
g_autoptr(udev_device) device = NULL;
BoltUdev *udev;
const char *action;
const char *syspath;
udev = BOLT_UDEV (user_data);
device = udev_monitor_receive_device (udev->monitor);
if (device == NULL)
return G_SOURCE_CONTINUE;
action = udev_device_get_action (device);
if (action == NULL)
return G_SOURCE_CONTINUE;
syspath = udev_device_get_syspath (device);
if (syspath == NULL)
return G_SOURCE_CONTINUE;
g_signal_emit (udev, signals[SIGNAL_UEVENT], 0,
action, device);
return G_SOURCE_CONTINUE;
}
static gboolean
bolt_udev_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
BoltUdev *udev = BOLT_UDEV (initable);
gboolean ok;
udev->udev = udev_new ();
if (udev->udev == NULL)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"udev: could not create udev handle");
return FALSE;
}
ok = setup_monitor (udev, udev->name,
udev->filter,
(GSourceFunc) handle_uevent_udev,
&udev->monitor, &udev->source,
error);
return ok;
}
/* public methods */
BoltUdev *
bolt_udev_new (const char *name,
const char * const *filter,
GError **error)
{
BoltUdev *udev;
udev = g_initable_new (BOLT_TYPE_UDEV,
NULL, error,
"name", name,
"filter", filter,
NULL);
return udev;
}
struct udev_enumerate *
bolt_udev_new_enumerate (BoltUdev *udev,
GError **error)
{
struct udev_enumerate *e;
g_return_val_if_fail (BOLT_IS_UDEV (udev), NULL);
e = udev_enumerate_new (udev->udev);
if (e == NULL)
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"could not enumerate udev: %s",
g_strerror (errno));
return e;
}
struct udev_device *
bolt_udev_device_new_from_syspath (BoltUdev *udev,
const char *syspath,
GError **error)
{
struct udev_device *dev;
g_return_val_if_fail (BOLT_IS_UDEV (udev), NULL);
g_return_val_if_fail (syspath != NULL, NULL);
dev = udev_device_new_from_syspath (udev->udev, syspath);
if (dev == NULL)
g_set_error (error, BOLT_ERROR, BOLT_ERROR_UDEV,
"could not create udev device: %s",
g_strerror (errno));
return dev;
}
/* thunderbolt specific helpers */
int
bolt_udev_count_hosts (BoltUdev *udev,
GError **error)
{
g_return_val_if_fail (BOLT_IS_UDEV (udev), -1);
return bolt_sysfs_count_hosts (udev->udev, error);
}
gboolean
bolt_udev_detect_force_power (BoltUdev *udev,
char **result,
GError **error)
{
struct udev_enumerate *e;
struct udev_list_entry *l, *devices;
g_return_val_if_fail (BOLT_IS_UDEV (udev), FALSE);
g_return_val_if_fail (result != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
e = bolt_udev_new_enumerate (udev, NULL);
udev_enumerate_add_match_subsystem (e, "wmi");
udev_enumerate_add_match_property (e, "DRIVER", "intel-wmi-thunderbolt");
udev_enumerate_scan_devices (e);
devices = udev_enumerate_get_list_entry (e);
udev_list_entry_foreach (l, devices)
{
g_autofree char *path = NULL;
const char *syspath;
syspath = udev_list_entry_get_name (l);
path = g_build_filename (syspath, "force_power", NULL);
if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
{
*result = g_steal_pointer (&path);
break;
}
}
udev_enumerate_unref (e);
return TRUE;
}
bolt-0.9.2/boltd/bolt-udev.h 0000664 0000000 0000000 00000003720 14174530510 0015642 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
/* forward declaration */
struct udev;
struct udev_device;
struct udev_enumerate;
/* BoltUdev - small udev abstraction */
#define BOLT_TYPE_UDEV bolt_udev_get_type ()
G_DECLARE_FINAL_TYPE (BoltUdev, bolt_udev, BOLT, UDEV, GObject);
BoltUdev * bolt_udev_new (const char *name,
const char * const *filter,
GError **error);
struct udev_enumerate * bolt_udev_new_enumerate (BoltUdev *udev,
GError **error);
struct udev_device * bolt_udev_device_new_from_syspath (BoltUdev *udev,
const char *syspath,
GError **error);
/* thunderbolt specific helpers */
int bolt_udev_count_hosts (BoltUdev *udev,
GError **error);
gboolean bolt_udev_detect_force_power (BoltUdev *udev,
char **path,
GError **error);
G_END_DECLS
bolt-0.9.2/boltd/bolt-watchdog.c 0000664 0000000 0000000 00000013720 14174530510 0016473 0 ustar 00root root 0000000 0000000 /*
* Copyright © 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-watchdog.h"
#include "bolt-log.h"
#include "bolt-str.h"
#include "bolt-time.h"
#include "bolt-unix.h"
#include
/* prototypes */
static void watchdog_initable_iface_init (GInitableIface *iface);
static gboolean bolt_watchdog_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error);
static gboolean bolt_watchdog_on_pulse (gpointer user_data);
/* */
struct _BoltWatchdog
{
GObject object;
/* */
guint64 timeout; /* the actual timeout (usec) */
guint64 pulse; /* the calculated pulse (sec) */
guint pulse_id; /* source id for the pulse */
};
enum {
PROP_0,
PROP_TIMEOUT,
PROP_PULSE,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = { NULL, };
G_DEFINE_TYPE_WITH_CODE (BoltWatchdog,
bolt_watchdog,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
watchdog_initable_iface_init));
static void
bolt_watchdog_finalize (GObject *object)
{
BoltWatchdog *dog = BOLT_WATCHDOG (object);
g_clear_handle_id (&dog->pulse_id, g_source_remove);
G_OBJECT_CLASS (bolt_watchdog_parent_class)->finalize (object);
}
static void
bolt_watchdog_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltWatchdog *dog = BOLT_WATCHDOG (object);
switch (prop_id)
{
case PROP_TIMEOUT:
g_value_set_uint64 (value, dog->timeout);
break;
case PROP_PULSE:
g_value_set_uint (value, dog->pulse);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_watchdog_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltWatchdog *dog = BOLT_WATCHDOG (object);
switch (prop_id)
{
case PROP_TIMEOUT:
dog->timeout = g_value_get_uint64 (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_watchdog_init (BoltWatchdog *dog)
{
}
static void
bolt_watchdog_class_init (BoltWatchdogClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = bolt_watchdog_get_property;
gobject_class->set_property = bolt_watchdog_set_property;
gobject_class->finalize = bolt_watchdog_finalize;
props[PROP_TIMEOUT] =
g_param_spec_uint64 ("timeout",
"Timeout", NULL,
0, G_MAXUINT64, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_PULSE] =
g_param_spec_uint ("pulse",
"Pulse", NULL,
0, G_MAXUINT - 1, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
}
static void
watchdog_initable_iface_init (GInitableIface *iface)
{
iface->init = bolt_watchdog_initialize;
}
static gboolean
bolt_watchdog_initialize (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
BoltWatchdog *dog = BOLT_WATCHDOG (initable);
guint64 quot, rem;
guint pulse;
guint tid;
int r;
r = bolt_sd_watchdog_enabled (&dog->timeout, error);
if (r < 0)
return FALSE;
else if (r == 0)
return TRUE;
quot = dog->timeout / G_USEC_PER_SEC;
rem = dog->timeout % G_USEC_PER_SEC;
if (quot < 2 || quot >= G_MAXUINT)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"invalid timeout: %" G_GUINT64_FORMAT, dog->timeout);
return FALSE;
}
if (rem != 0)
bolt_warn (LOG_TOPIC ("watchdog"),
"sub-second precision timeout: "
"%" G_GUINT64_FORMAT ". Rounding down.", rem);
/* we send a pulse/ping right in the middle of the timeout
* period to ensure we never miss one */
pulse = quot / 2;
tid = g_timeout_add_seconds (pulse, bolt_watchdog_on_pulse, dog);
dog->pulse_id = tid;
dog->pulse = pulse;
/* we have an active timeout enabled */
bolt_info (LOG_TOPIC ("watchdog"), "enabled [pulse: %lus]", pulse);
return TRUE;
}
/* internal methods */
static gboolean
bolt_watchdog_on_pulse (gpointer user_data)
{
g_autoptr(GError) err = NULL;
gboolean ok;
gboolean sent;
ok = bolt_sd_notify_literal ("WATCHDOG=1", &sent, &err);
if (!ok)
bolt_warn_err (err, LOG_TOPIC ("watchdog"), "failed to send ping");
else
bolt_debug (LOG_TOPIC ("watchdog"), "ping [sent: %s]",
bolt_yesno (sent));
return G_SOURCE_CONTINUE;
}
/* public methods */
BoltWatchdog *
bolt_watchdog_new (GError **error)
{
BoltWatchdog *watchdog;
watchdog = g_initable_new (BOLT_TYPE_WATCHDOG,
NULL, error,
NULL);
return watchdog;
}
bolt-0.9.2/boltd/bolt-watchdog.h 0000664 0000000 0000000 00000002077 14174530510 0016503 0 ustar 00root root 0000000 0000000 /*
* Copyright © 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
/* BoltWatchdog - Idle and status tracking */
#define BOLT_TYPE_WATCHDOG bolt_watchdog_get_type ()
G_DECLARE_FINAL_TYPE (BoltWatchdog, bolt_watchdog, BOLT, WATCHDOG, GObject);
BoltWatchdog * bolt_watchdog_new (GError **error);
G_END_DECLS
bolt-0.9.2/cli/ 0000775 0000000 0000000 00000000000 14174530510 0013231 5 ustar 00root root 0000000 0000000 bolt-0.9.2/cli/bolt-client.c 0000664 0000000 0000000 00000106404 14174530510 0015616 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "bolt-client.h"
#include "bolt-device.h"
#include "bolt-error.h"
#include "bolt-names.h"
#include "bolt-str.h"
#include
static void handle_dbus_device_added (GObject *self,
GDBusProxy *bus_proxy,
GVariant *params);
static void handle_dbus_device_removed (GObject *self,
GDBusProxy *bus_proxy,
GVariant *params);
static void handle_dbus_domain_added (GObject *self,
GDBusProxy *bus_proxy,
GVariant *params);
static void handle_dbus_domain_removed (GObject *self,
GDBusProxy *bus_proxy,
GVariant *params);
struct _BoltClient
{
BoltProxy parent;
};
enum {
PROP_0,
/* D-Bus Props */
PROP_VERSION,
PROP_PROBING,
PROP_POLICY,
PROP_SECURITY,
PROP_AUTHMODE,
PROP_POWERSTATE,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = {NULL, };
enum {
SIGNAL_DEVICE_ADDED,
SIGNAL_DEVICE_REMOVED,
SIGNAL_DOMAIN_ADDED,
SIGNAL_DOMAIN_REMOVED,
SIGNAL_LAST
};
static guint signals[SIGNAL_LAST] = {0};
G_DEFINE_TYPE (BoltClient,
bolt_client,
BOLT_TYPE_PROXY);
static const BoltProxySignal *
bolt_client_get_dbus_signals (guint *n)
{
static BoltProxySignal dbus_signals[] = {
{"DeviceAdded", handle_dbus_device_added},
{"DeviceRemoved", handle_dbus_device_removed},
{"DomainAdded", handle_dbus_domain_added},
{"DomainRemoved", handle_dbus_domain_removed},
};
*n = G_N_ELEMENTS (dbus_signals);
return dbus_signals;
}
static void
bolt_client_class_init (BoltClientClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
BoltProxyClass *proxy_class = BOLT_PROXY_CLASS (klass);
gobject_class->get_property = bolt_proxy_property_getter;
gobject_class->set_property = bolt_proxy_property_setter;
proxy_class->get_dbus_signals = bolt_client_get_dbus_signals;
props[PROP_VERSION]
= g_param_spec_uint ("version", "Version",
"D-Bus API revision.",
0, G_MAXUINT, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_PROBING]
= g_param_spec_boolean ("probing", "Probing",
"Is the daemon processing new devices?",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_POLICY]
= g_param_spec_enum ("default-policy", "DefaultPolicy",
"Policy to use if none was specified.",
BOLT_TYPE_POLICY,
BOLT_POLICY_UNKNOWN,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_SECURITY]
= g_param_spec_enum ("security-level", "SecurityLevel",
"The global security level.",
BOLT_TYPE_SECURITY,
BOLT_SECURITY_UNKNOWN,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_AUTHMODE] =
g_param_spec_flags ("auth-mode", "AuthMode",
"Current authentication mode.",
BOLT_TYPE_AUTH_MODE,
BOLT_AUTH_ENABLED,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_POWERSTATE] =
g_param_spec_enum ("power-state", "PowerState",
"Current force power state.",
BOLT_TYPE_POWER_STATE,
BOLT_FORCE_POWER_UNSET,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
/* signals */
signals[SIGNAL_DEVICE_ADDED] =
g_signal_new ("device-added",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
1, G_TYPE_STRING);
signals[SIGNAL_DEVICE_REMOVED] =
g_signal_new ("device-removed",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
1, G_TYPE_STRING);
signals[SIGNAL_DOMAIN_ADDED] =
g_signal_new ("domain-added",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
1, G_TYPE_STRING);
signals[SIGNAL_DOMAIN_REMOVED] =
g_signal_new ("domain-removed",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE,
1, G_TYPE_STRING);
}
static void
bolt_client_init (BoltClient *cli)
{
/* we don't want this to be optimized away */
volatile GQuark error_domain;
/* initialize error mapping */
error_domain = BOLT_ERROR;
(error_domain); /* To avoid -Wunused-but-set-variable */
}
/* dbus signals */
static void
handle_dbus_device_added (GObject *self, GDBusProxy *bus_proxy, GVariant *params)
{
BoltClient *cli = BOLT_CLIENT (self);
const char *opath = NULL;
g_variant_get_child (params, 0, "&o", &opath);
g_signal_emit (cli, signals[SIGNAL_DEVICE_ADDED], 0, opath);
}
static void
handle_dbus_device_removed (GObject *self, GDBusProxy *bus_proxy, GVariant *params)
{
BoltClient *cli = BOLT_CLIENT (self);
const char *opath = NULL;
g_variant_get_child (params, 0, "&o", &opath);
g_signal_emit (cli, signals[SIGNAL_DEVICE_REMOVED], 0, opath);
}
static void
handle_dbus_domain_added (GObject *self, GDBusProxy *bus_proxy, GVariant *params)
{
BoltClient *cli = BOLT_CLIENT (self);
const char *opath = NULL;
g_variant_get_child (params, 0, "&o", &opath);
g_signal_emit (cli, signals[SIGNAL_DOMAIN_ADDED], 0, opath);
}
static void
handle_dbus_domain_removed (GObject *self, GDBusProxy *bus_proxy, GVariant *params)
{
BoltClient *cli = BOLT_CLIENT (self);
const char *opath = NULL;
g_variant_get_child (params, 0, "&o", &opath);
g_signal_emit (cli, signals[SIGNAL_DOMAIN_REMOVED], 0, opath);
}
/* public methods */
BoltClient *
bolt_client_new (GError **error)
{
BoltClient *cli;
GDBusConnection *bus;
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
if (bus == NULL)
{
g_prefix_error (error, "Error connecting to D-Bus: ");
return FALSE;
}
cli = g_initable_new (BOLT_TYPE_CLIENT,
NULL, error,
"g-flags", G_DBUS_PROXY_FLAGS_NONE,
"g-connection", bus,
"g-name", BOLT_DBUS_NAME,
"g-object-path", BOLT_DBUS_PATH,
"g-interface-name", BOLT_DBUS_INTERFACE,
NULL);
g_object_unref (bus);
return cli;
}
static void
got_the_client (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
GError *error = NULL;
GObject *obj;
obj = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, &error);
if (obj == NULL)
{
/* error ownership gets transferred to the task */
g_task_return_error (task, error);
return;
}
g_task_return_pointer (task, obj, g_object_unref);
}
static void
got_the_bus (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
GError *error = NULL;
GTask *task = user_data;
GCancellable *cancellable;
GDBusConnection *bus;
bus = g_bus_get_finish (res, &error);
if (bus == NULL)
{
g_prefix_error (&error, "could not connect to D-Bus: ");
/* error ownership gets transferred to the task */
g_task_return_error (task, error);
g_object_unref (task);
return;
}
cancellable = g_task_get_cancellable (task);
g_async_initable_new_async (BOLT_TYPE_CLIENT,
G_PRIORITY_DEFAULT,
cancellable,
got_the_client, task,
"g-flags", G_DBUS_PROXY_FLAGS_NONE,
"g-connection", bus,
"g-name", BOLT_DBUS_NAME,
"g-object-path", BOLT_DBUS_PATH,
"g-interface-name", BOLT_DBUS_INTERFACE,
NULL);
g_object_unref (bus);
}
void
bolt_client_new_async (GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (callback == NULL);
task = g_task_new (NULL, cancellable, callback, user_data);
g_bus_get (G_BUS_TYPE_SYSTEM, cancellable, got_the_bus, task);
}
BoltClient *
bolt_client_new_finish (GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (G_IS_TASK (res), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK (res), error);
}
GPtrArray *
bolt_client_list_domains (BoltClient *client,
GCancellable *cancel,
GError **error)
{
g_autoptr(GVariant) val = NULL;
g_autoptr(GPtrArray) domains = NULL;
g_autoptr(GVariantIter) iter = NULL;
GDBusConnection *bus = NULL;
const char *d;
g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL);
g_return_val_if_fail (!cancel || G_IS_CANCELLABLE (cancel), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client),
"ListDomains",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancel,
error);
if (val == NULL)
return NULL;
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client));
domains = g_ptr_array_new_with_free_func (g_object_unref);
g_variant_get (val, "(ao)", &iter);
while (g_variant_iter_loop (iter, "&o", &d, NULL))
{
BoltDomain *dom;
dom = bolt_domain_new_for_object_path (bus, d, cancel, error);
if (dom == NULL)
return NULL;
g_ptr_array_add (domains, dom);
}
return g_steal_pointer (&domains);
}
BoltDomain *
bolt_client_find_domain (BoltClient *client,
const char *name,
GError **error)
{
g_autoptr(GPtrArray) domains = NULL;
domains = bolt_client_list_domains (client, NULL, error);
if (domains == NULL)
return NULL;
for (guint i = 0; i < domains->len; i++)
{
BoltDomain *dom = g_ptr_array_index (domains, i);
if (bolt_streq (name, bolt_domain_get_uid (dom)) ||
bolt_streq (name, bolt_domain_get_id (dom)))
return g_object_ref (dom);
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"could not find domain matching '%s'", name);
return NULL;
}
GPtrArray *
bolt_client_list_devices (BoltClient *client,
GCancellable *cancel,
GError **error)
{
g_autoptr(GVariant) val = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GVariantIter) iter = NULL;
GDBusConnection *bus = NULL;
const char *d;
g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL);
g_return_val_if_fail (!cancel || G_IS_CANCELLABLE (cancel), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client),
"ListDevices",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancel,
error);
if (val == NULL)
return NULL;
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client));
devices = g_ptr_array_new_with_free_func (g_object_unref);
g_variant_get (val, "(ao)", &iter);
while (g_variant_iter_loop (iter, "&o", &d, NULL))
{
BoltDevice *dev;
dev = bolt_device_new_for_object_path (bus, d, cancel, error);
if (dev == NULL)
return NULL;
g_ptr_array_add (devices, dev);
}
return g_steal_pointer (&devices);
}
GPtrArray *
bolt_client_list_parents (BoltClient *client,
BoltDevice *device,
GCancellable *cancellable,
GError **error)
{
g_autoptr(BoltDevice) dev = NULL;
g_autoptr(GPtrArray) uuids = NULL;
const char *parent;
uuids = g_ptr_array_new_full (16, g_object_unref);
dev = g_object_ref (device);
while ((parent = bolt_device_get_parent (dev)) != NULL)
{
g_autofree char *up = g_strdup (parent);
g_clear_object (&dev); /* 'parent' can be invalid */
parent = NULL;
dev = bolt_client_get_device (client,
up,
NULL,
error);
if (dev == NULL)
return NULL;
g_ptr_array_add (uuids, g_object_ref (dev));
}
return g_steal_pointer (&uuids);
}
BoltDevice *
bolt_client_get_device (BoltClient *client,
const char *uid,
GCancellable *cancel,
GError **error)
{
g_autoptr(GVariant) val = NULL;
g_autoptr(GError) err = NULL;
BoltDevice *dev = NULL;
GDBusConnection *bus = NULL;
const char *opath = NULL;
g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL);
g_return_val_if_fail (uid != NULL, NULL);
g_return_val_if_fail (!cancel || G_IS_CANCELLABLE (cancel), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client),
"DeviceByUid",
g_variant_new ("(s)", uid),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancel,
&err);
if (val == NULL)
{
bolt_error_propagate_stripped (error, &err);
return NULL;
}
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client));
g_variant_get (val, "(&o)", &opath);
if (opath == NULL)
return NULL;
dev = bolt_device_new_for_object_path (bus, opath, cancel, error);
return dev;
}
BoltDevice *
bolt_client_find_device (BoltClient *client,
const char *name,
GError **error)
{
g_autoptr(GPtrArray) devices = NULL;
devices = bolt_client_list_devices (client, NULL, error);
if (devices == NULL)
return NULL;
for (guint i = 0; i < devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (devices, i);
if (bolt_streq (name, bolt_device_get_uid (dev)) ||
bolt_streq (name, bolt_device_get_name (dev)))
return g_object_ref (dev);
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"could not find device matching '%s'", name);
return NULL;
}
BoltDevice *
bolt_client_enroll_device (BoltClient *client,
const char *uid,
BoltPolicy policy,
BoltAuthCtrl flags,
GError **error)
{
g_autoptr(GVariant) val = NULL;
g_autoptr(GError) err = NULL;
g_autofree char *fstr = NULL;
BoltDevice *dev = NULL;
GDBusConnection *bus = NULL;
GVariant *params = NULL;
const char *opath = NULL;
const char *pstr;
g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL);
g_return_val_if_fail (uid != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
pstr = bolt_enum_to_string (BOLT_TYPE_POLICY, policy, error);
if (pstr == NULL)
return NULL;
fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, error);
if (fstr == NULL)
return NULL;
params = g_variant_new ("(sss)", uid, pstr, fstr);
val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client),
"EnrollDevice",
params,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&err);
if (val == NULL)
{
bolt_error_propagate_stripped (error, &err);
return NULL;
}
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client));
g_variant_get (val, "(&o)", &opath);
if (opath == NULL)
return NULL;
dev = bolt_device_new_for_object_path (bus, opath, NULL, error);
return dev;
}
void
bolt_client_enroll_device_async (BoltClient *client,
const char *uid,
BoltPolicy policy,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autofree char *fstr = NULL;
GError *err = NULL;
GVariant *params;
const char *pstr;
g_return_if_fail (BOLT_IS_CLIENT (client));
g_return_if_fail (uid != NULL);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (callback == NULL);
pstr = bolt_enum_to_string (BOLT_TYPE_POLICY, policy, &err);
if (pstr == NULL)
{
g_task_report_error (client, callback, user_data, NULL, err);
return;
}
fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, &err);
if (fstr == NULL)
{
g_task_report_error (client, callback, user_data, NULL, err);
return;
}
params = g_variant_new ("(sss)", uid, pstr, fstr);
g_dbus_proxy_call (G_DBUS_PROXY (client),
"EnrollDevice",
params,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
callback,
user_data);
}
gboolean
bolt_client_enroll_device_finish (BoltClient *client,
GAsyncResult *res,
char **path,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (client), res, &err);
if (val == NULL)
{
bolt_error_propagate_stripped (error, &err);
return FALSE;
}
if (path != NULL)
g_variant_get (val, "(o)", path);
return TRUE;
}
typedef struct OpData
{
const char *iface; /* Manager or Device */
const char *method; /* Enroll or Authorize */
char *path; /* object path */
GVariant *params; /* parameters */
} OpData;
static OpData *
op_data_new_enroll (const char *uid,
const char *policy,
const char *flags)
{
GVariant *params;
OpData *op;
params = g_variant_new ("(sss)", uid, policy, flags);
op = g_slice_new (OpData);
op->iface = BOLT_DBUS_INTERFACE;
op->method = "EnrollDevice";
op->params = g_variant_ref_sink (params);
op->path = g_strdup (BOLT_DBUS_PATH);
return op;
}
static OpData *
op_data_new_authorize (const char *uid,
const char *flags)
{
OpData *op = NULL;
GVariant *params;
char *path;
path = bolt_gen_object_path (BOLT_DBUS_PATH_DEVICES, uid);
params = g_variant_new ("(s)", flags);
op = g_slice_new (OpData);
op->iface = BOLT_DBUS_DEVICE_INTERFACE;
op->method = "Authorize";
op->params = g_variant_ref_sink (params);
op->path = path; /* takes ownership */
return op;
}
static void
op_data_free (OpData *op)
{
g_clear_pointer (&op->params, g_variant_unref);
g_clear_pointer (&op->path, g_free);
g_slice_free (OpData, op);
}
static void
op_queue_free (GQueue *queue)
{
g_queue_free_full (queue, (GDestroyNotify) op_data_free);
}
static void allop_one_done (GObject *source_object,
GAsyncResult *res,
gpointer user_data);
static gboolean
allop_continue (BoltClient *client, GTask *task, GQueue *ops)
{
GDBusConnection *bus;
GCancellable *cancel;
OpData *op;
cancel = g_task_get_cancellable (task);
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client));
op = g_queue_pop_head (ops);
if (op == NULL)
return TRUE;
g_dbus_connection_call (bus,
BOLT_DBUS_NAME,
op->path,
op->iface,
op->method,
op->params,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancel,
allop_one_done,
task);
op_data_free (op);
return FALSE;
}
static void
allop_one_done (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GVariant) val = NULL;
BoltClient *client;
GDBusConnection *bus;
gboolean done;
GError *err = NULL;
GQueue *ops;
GTask *task;
task = G_TASK (user_data);
ops = g_task_get_task_data (task);
client = g_task_get_source_object (task);
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client));
val = g_dbus_connection_call_finish (bus, res, &err);
if (val == NULL)
{
g_task_return_error (task, err); /* takes ownership */
g_object_unref (task);
/* we are done (albeit with an error) */
return;
}
done = allop_continue (client, task, ops);
if (done)
{
/* we are done */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
}
void
bolt_client_enroll_all_async (BoltClient *client,
GPtrArray *uuids,
BoltPolicy policy,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autofree char *fstr = NULL;
GError *err = NULL;
const char *pstr;
GQueue *ops;
GTask *task;
g_return_if_fail (BOLT_IS_CLIENT (client));
g_return_if_fail (uuids != NULL && uuids->len > 0);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (callback != NULL);
pstr = bolt_enum_to_string (BOLT_TYPE_POLICY, policy, &err);
if (pstr == NULL)
{
g_task_report_error (client, callback, user_data, NULL, err);
return;
}
fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, &err);
if (fstr == NULL)
{
g_task_report_error (client, callback, user_data, NULL, err);
return;
}
task = g_task_new (client, cancellable, callback, user_data);
g_task_set_return_on_cancel (task, TRUE);
ops = g_queue_new ();
g_task_set_task_data (task, ops, (GDestroyNotify) op_queue_free);
for (guint i = 0; i < uuids->len; i++)
{
const char *uid = g_ptr_array_index (uuids, i);
OpData *op;
op = op_data_new_enroll (uid, pstr, fstr);
g_queue_push_tail (ops, op);
}
allop_continue (client, task, ops);
}
gboolean
bolt_client_enroll_all_finish (BoltClient *client,
GAsyncResult *res,
GError **error)
{
g_autoptr(GError) err = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE);
g_return_val_if_fail (g_task_is_valid (res, client), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ok = g_task_propagate_boolean (G_TASK (res), &err);
if (!ok)
bolt_error_propagate_stripped (error, &err);
return ok;
}
void
bolt_client_authorize_all_async (BoltClient *client,
GPtrArray *uuids,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autofree char *fstr = NULL;
GError *err = NULL;
GQueue *ops;
GTask *task;
g_return_if_fail (BOLT_IS_CLIENT (client));
g_return_if_fail (uuids != NULL && uuids->len > 0);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (callback != NULL);
fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, &err);
if (fstr == NULL)
{
g_task_report_error (client, callback, user_data, NULL, err);
return;
}
task = g_task_new (client, cancellable, callback, user_data);
g_task_set_return_on_cancel (task, TRUE);
ops = g_queue_new ();
g_task_set_task_data (task, ops, (GDestroyNotify) op_queue_free);
for (guint i = 0; i < uuids->len; i++)
{
const char *uid = g_ptr_array_index (uuids, i);
OpData *op;
op = op_data_new_authorize (uid, fstr);
g_queue_push_tail (ops, op);
}
allop_continue (client, task, ops);
}
gboolean
bolt_client_authorize_all_finish (BoltClient *client,
GAsyncResult *res,
GError **error)
{
g_autoptr(GError) err = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE);
g_return_val_if_fail (g_task_is_valid (res, client), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ok = g_task_propagate_boolean (G_TASK (res), &err);
if (!ok)
bolt_error_propagate_stripped (error, &err);
return ok;
}
void
bolt_client_connect_all_async (BoltClient *client,
GPtrArray *devices,
BoltPolicy policy,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autofree char *fstr = NULL;
GError *err = NULL;
const char *pstr;
GQueue *ops;
GTask *task;
g_return_if_fail (BOLT_IS_CLIENT (client));
g_return_if_fail (devices != NULL && devices->len > 0);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (callback != NULL);
pstr = bolt_enum_to_string (BOLT_TYPE_POLICY, policy, &err);
if (pstr == NULL)
{
g_task_report_error (client, callback, user_data, NULL, err);
return;
}
fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, &err);
if (fstr == NULL)
{
g_task_report_error (client, callback, user_data, NULL, err);
return;
}
task = g_task_new (client, cancellable, callback, user_data);
g_task_set_return_on_cancel (task, TRUE);
ops = g_queue_new ();
g_task_set_task_data (task, ops, (GDestroyNotify) op_queue_free);
for (guint i = 0; i < devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (devices, i);
const char *uid = bolt_device_get_uid (dev);
OpData *op;
if (bolt_device_is_stored (dev))
op = op_data_new_authorize (uid, fstr);
else
op = op_data_new_enroll (uid, pstr, fstr);
g_queue_push_tail (ops, op);
}
allop_continue (client, task, ops);
}
gboolean
bolt_client_connect_all_finish (BoltClient *client,
GAsyncResult *res,
GError **error)
{
g_autoptr(GError) err = NULL;
gboolean ok;
g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE);
g_return_val_if_fail (g_task_is_valid (res, client), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ok = g_task_propagate_boolean (G_TASK (res), &err);
if (!ok)
bolt_error_propagate_stripped (error, &err);
return ok;
}
gboolean
bolt_client_forget_device (BoltClient *client,
const char *uid,
GError **error)
{
g_autoptr(GVariant) val = NULL;
g_autoptr(GError) err = NULL;
g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE);
g_return_val_if_fail (uid != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
val = g_dbus_proxy_call_sync (G_DBUS_PROXY (client),
"ForgetDevice",
g_variant_new ("(s)", uid),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&err);
if (val == NULL)
{
bolt_error_propagate_stripped (error, &err);
return FALSE;
}
return TRUE;
}
void
bolt_client_forget_device_async (BoltClient *client,
const char *uid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (BOLT_IS_CLIENT (client));
g_return_if_fail (uid == NULL);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (callback == NULL);
g_dbus_proxy_call (G_DBUS_PROXY (client),
"ForgetDevice",
g_variant_new ("(s)", uid),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
callback,
user_data);
}
gboolean
bolt_client_forget_device_finish (BoltClient *client,
GAsyncResult *res,
GError **error)
{
g_autoptr(GVariant) val = NULL;
g_autoptr(GError) err = NULL;
g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (client), res, &err);
if (val == NULL)
{
bolt_error_propagate_stripped (error, &err);
return FALSE;
}
return TRUE;
}
BoltPower *
bolt_client_new_power_client (BoltClient *client,
GCancellable *cancellable,
GError **error)
{
GDBusConnection *bus = NULL;
g_return_val_if_fail (BOLT_IS_CLIENT (client), NULL);
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (client));
return bolt_power_new_for_object_path (bus, cancellable, error);
}
/* getter */
guint
bolt_client_get_version (BoltClient *client)
{
guint val = 0;
g_return_val_if_fail (BOLT_IS_CLIENT (client), val);
val = bolt_proxy_get_uint32_by_pspec (client, props[PROP_VERSION]);
return val;
}
gboolean
bolt_client_is_probing (BoltClient *client)
{
gboolean val = FALSE;
g_return_val_if_fail (BOLT_IS_CLIENT (client), val);
val = bolt_proxy_get_bool_by_pspec (client, props[PROP_PROBING]);
return val;
}
BoltPolicy
bolt_client_get_policy (BoltClient *client)
{
gint val = BOLT_POLICY_UNKNOWN;
g_return_val_if_fail (BOLT_IS_CLIENT (client), val);
val = bolt_proxy_get_enum_by_pspec (client, props[PROP_POLICY]);
return val;
}
BoltSecurity
bolt_client_get_security (BoltClient *client)
{
gint val = BOLT_SECURITY_UNKNOWN;
g_return_val_if_fail (BOLT_IS_CLIENT (client), val);
val = bolt_proxy_get_enum_by_pspec (client, props[PROP_SECURITY]);
return val;
}
BoltAuthMode
bolt_client_get_authmode (BoltClient *client)
{
guint val = BOLT_AUTH_DISABLED;
g_return_val_if_fail (BOLT_IS_CLIENT (client), val);
val = bolt_proxy_get_flags_by_pspec (client, props[PROP_AUTHMODE]);
return val;
}
BoltPowerState
bolt_client_get_power_state (BoltClient *client)
{
gint val = BOLT_FORCE_POWER_UNSET;
g_return_val_if_fail (BOLT_IS_CLIENT (client), val);
val = bolt_proxy_get_enum_by_pspec (client, props[PROP_POWERSTATE]);
return val;
}
void
bolt_client_set_authmode_async (BoltClient *client,
BoltAuthMode mode,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_auto(GValue) val = G_VALUE_INIT;
g_return_if_fail (BOLT_IS_CLIENT (client));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (callback != NULL);
g_value_init (&val, BOLT_TYPE_AUTH_MODE);
g_value_set_flags (&val, mode);
bolt_proxy_set_async (BOLT_PROXY (client),
props[PROP_AUTHMODE],
&val,
cancellable,
callback,
user_data);
}
gboolean
bolt_client_set_authmode_finish (BoltClient *client,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (BOLT_IS_CLIENT (client), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return bolt_proxy_set_property_finish (res, error);
}
/* utility functions */
static gint
device_sort_by_syspath (gconstpointer ap,
gconstpointer bp,
gpointer data)
{
BoltDevice *a = BOLT_DEVICE (*((BoltDevice **) ap));
BoltDevice *b = BOLT_DEVICE (*((BoltDevice **) bp));
gint sort_order = GPOINTER_TO_INT (data);
const char *pa;
const char *pb;
pa = bolt_device_get_syspath (a);
pb = bolt_device_get_syspath (b);
return sort_order * g_strcmp0 (pa, pb);
}
void
bolt_devices_sort_by_syspath (GPtrArray *devices,
gboolean reverse)
{
gpointer sort_order = GINT_TO_POINTER (reverse ? -1 : 1);
if (devices == NULL)
return;
g_ptr_array_sort_with_data (devices,
device_sort_by_syspath,
sort_order);
}
bolt-0.9.2/cli/bolt-client.h 0000664 0000000 0000000 00000020024 14174530510 0015614 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-device.h"
#include "bolt-domain.h"
#include "bolt-power.h"
#include "bolt-proxy.h"
G_BEGIN_DECLS
#define BOLT_TYPE_CLIENT bolt_client_get_type ()
G_DECLARE_FINAL_TYPE (BoltClient, bolt_client, BOLT, CLIENT, BoltProxy);
BoltClient * bolt_client_new (GError **error);
void bolt_client_new_async (GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
BoltClient * bolt_client_new_finish (GAsyncResult *res,
GError **error);
GPtrArray * bolt_client_list_domains (BoltClient *client,
GCancellable *cancellable,
GError **error);
BoltDomain * bolt_client_find_domain (BoltClient *client,
const char *name,
GError **error);
GPtrArray * bolt_client_list_devices (BoltClient *client,
GCancellable *cancellable,
GError **error);
GPtrArray * bolt_client_list_parents (BoltClient *client,
BoltDevice *device,
GCancellable *cancellable,
GError **error);
BoltDevice * bolt_client_get_device (BoltClient *client,
const char *uid,
GCancellable *cancellable,
GError **error);
BoltDevice * bolt_client_find_device (BoltClient *client,
const char *name,
GError **error);
BoltDevice * bolt_client_enroll_device (BoltClient *client,
const char *uid,
BoltPolicy policy,
BoltAuthCtrl flags,
GError **error);
void bolt_client_enroll_device_async (BoltClient *client,
const char *uid,
BoltPolicy policy,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean bolt_client_enroll_device_finish (BoltClient *client,
GAsyncResult *res,
char **path,
GError **error);
void bolt_client_enroll_all_async (BoltClient *client,
GPtrArray *uuids,
BoltPolicy policy,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean bolt_client_enroll_all_finish (BoltClient *client,
GAsyncResult *res,
GError **error);
void bolt_client_authorize_all_async (BoltClient *client,
GPtrArray *uuids,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean bolt_client_authorize_all_finish (BoltClient *client,
GAsyncResult *res,
GError **error);
void bolt_client_connect_all_async (BoltClient *client,
GPtrArray *devices,
BoltPolicy policy,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean bolt_client_connect_all_finish (BoltClient *client,
GAsyncResult *res,
GError **error);
gboolean bolt_client_forget_device (BoltClient *client,
const char *uid,
GError **error);
void bolt_client_forget_device_async (BoltClient *client,
const char *uid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean bolt_client_forget_device_finish (BoltClient *client,
GAsyncResult *res,
GError **error);
BoltPower * bolt_client_new_power_client (BoltClient *client,
GCancellable *cancellable,
GError **error);
/* getter */
guint bolt_client_get_version (BoltClient *client);
gboolean bolt_client_is_probing (BoltClient *client);
BoltPolicy bolt_client_get_policy (BoltClient *client);
BoltSecurity bolt_client_get_security (BoltClient *client);
BoltAuthMode bolt_client_get_authmode (BoltClient *client);
BoltPowerState bolt_client_get_power_state (BoltClient *client);
/* setter */
void bolt_client_set_authmode_async (BoltClient *client,
BoltAuthMode mode,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean bolt_client_set_authmode_finish (BoltClient *client,
GAsyncResult *res,
GError **error);
/* utility functions */
void bolt_devices_sort_by_syspath (GPtrArray *devices,
gboolean reverse);
G_END_DECLS
bolt-0.9.2/cli/bolt-device.c 0000664 0000000 0000000 00000037441 14174530510 0015603 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-device.h"
#include "bolt-enums.h"
#include "bolt-error.h"
#include "bolt-names.h"
#include "bolt-wire.h"
#include
struct _BoltDevice
{
BoltProxy parent;
};
enum {
PROP_0,
/* D-Bus Props */
PROP_UID,
PROP_NAME,
PROP_VENDOR,
PROP_GEN,
PROP_TYPE,
PROP_STATUS,
PROP_AUTHFLAGS,
PROP_PARENT,
PROP_SYSPATH,
PROP_DOMAIN,
PROP_CONNTIME,
PROP_AUTHTIME,
PROP_LINKSPEED,
PROP_STORED,
PROP_POLICY,
PROP_KEY,
PROP_STORETIME,
PROP_LABEL,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = {NULL, };
G_DEFINE_TYPE (BoltDevice,
bolt_device,
BOLT_TYPE_PROXY);
static void
bolt_device_class_init (BoltDeviceClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = bolt_proxy_property_getter;
gobject_class->set_property = bolt_proxy_property_setter;
props[PROP_UID] =
g_param_spec_string ("uid", "Uid",
"The unique identifier.",
"unknown",
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_NAME] =
g_param_spec_string ("name", "Name",
"Human readable device name.",
"unknown",
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_VENDOR] =
g_param_spec_string ("vendor", "Vendor",
"The name of the device vendor",
"unknown",
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_GEN] =
g_param_spec_uint ("generation", "Generation",
"The generation of the controller chip.",
0, G_MAXUINT, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_TYPE] =
g_param_spec_enum ("type", "Type",
"The type, i.e. host or peripheral.",
BOLT_TYPE_DEVICE_TYPE,
BOLT_DEVICE_PERIPHERAL,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_STATUS] =
g_param_spec_enum ("status", "Status",
"The device status.",
BOLT_TYPE_STATUS,
BOLT_STATUS_DISCONNECTED,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_AUTHFLAGS] =
g_param_spec_flags ("authflags", "AuthFlags",
"Flags describing the authentication state.",
BOLT_TYPE_AUTH_FLAGS,
BOLT_AUTH_NONE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_PARENT] =
g_param_spec_string ("parent", "Parent",
"Unique identifier of the parent.",
"unknown",
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_SYSPATH] =
g_param_spec_string ("syspath", "SysfsPath",
"The sysfs path of the udev device.",
"unknown",
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_DOMAIN] =
g_param_spec_string ("domain", "Domain",
"Unique id of the corresponding domain.",
"unknown",
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_CONNTIME] =
g_param_spec_uint64 ("conntime", "ConnectTime",
"When was the device connected?",
0, G_MAXUINT64, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_AUTHTIME] =
g_param_spec_uint64 ("authtime", "AuthorizeTime",
"When was the device authorized?",
0, G_MAXUINT64, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_LINKSPEED] =
g_param_spec_boxed ("linkspeed", "LinkSpeed",
"The speed to the parent",
BOLT_TYPE_LINK_SPEED,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_STORED] =
g_param_spec_boolean ("stored", "Stored",
"Is the device recorded in the database?",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_POLICY] =
g_param_spec_enum ("policy", "Policy",
"What to do when the device is connected?",
BOLT_TYPE_POLICY,
BOLT_POLICY_DEFAULT,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_KEY] =
g_param_spec_enum ("key", "Key",
"State of the device key.",
BOLT_TYPE_KEY_STATE,
BOLT_KEY_MISSING,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_STORETIME] =
g_param_spec_uint64 ("storetime", "StoreTime",
"When was the device stored?",
0, G_MAXUINT64, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_LABEL] =
g_param_spec_string ("label", "Label",
"The name given by bolt or the user.",
NULL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
}
static void
bolt_device_init (BoltDevice *mgr)
{
}
/* public methods */
BoltDevice *
bolt_device_new_for_object_path (GDBusConnection *bus,
const char *path,
GCancellable *cancel,
GError **error)
{
g_autoptr(BoltDevice) dev = NULL;
gboolean ok;
g_return_val_if_fail (G_IS_DBUS_CONNECTION (bus), NULL);
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (!cancel || G_IS_CANCELLABLE (cancel), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
dev = g_initable_new (BOLT_TYPE_DEVICE,
cancel, error,
"g-flags", G_DBUS_PROXY_FLAGS_NONE,
"g-connection", bus,
"g-name", BOLT_DBUS_NAME,
"g-object-path", path,
"g-interface-name", BOLT_DBUS_DEVICE_INTERFACE,
NULL);
if (dev == NULL)
return NULL;
ok = bolt_proxy_set_wireconv (BOLT_PROXY (dev),
props[PROP_LINKSPEED],
"linkspeed",
bolt_link_speed_to_wire,
bolt_link_speed_from_wire,
error);
if (!ok)
return NULL;
return g_steal_pointer (&dev);
}
gboolean
bolt_device_authorize (BoltDevice *dev,
BoltAuthCtrl flags,
GCancellable *cancel,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *fstr = NULL;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
g_return_val_if_fail (!cancel || G_IS_CANCELLABLE (cancel), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, error);
if (fstr == NULL)
return FALSE;
g_dbus_proxy_call_sync (G_DBUS_PROXY (dev),
"Authorize",
g_variant_new ("(s)", fstr),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancel,
&err);
if (err != NULL)
return bolt_error_propagate_stripped (error, &err);
return TRUE;
}
void
bolt_device_authorize_async (BoltDevice *dev,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GError *err = NULL;
g_autofree char *fstr = NULL;
g_return_if_fail (BOLT_IS_DEVICE (dev));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (callback != NULL);
fstr = bolt_flags_to_string (BOLT_TYPE_AUTH_CTRL, flags, &err);
if (fstr == NULL)
{
g_task_report_error (dev, callback, user_data, NULL, err);
return;
}
g_dbus_proxy_call (G_DBUS_PROXY (dev),
"Authorize",
g_variant_new ("(s)", fstr),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
callback,
user_data);
}
gboolean
bolt_device_authorize_finish (BoltDevice *dev,
GAsyncResult *res,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (dev), res, &err);
if (val == NULL)
{
bolt_error_propagate_stripped (error, &err);
return FALSE;
}
return TRUE;
}
const char *
bolt_device_get_uid (BoltDevice *dev)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
str = bolt_proxy_get_string_by_pspec (dev, props[PROP_UID]);
return str;
}
const char *
bolt_device_get_name (BoltDevice *dev)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
str = bolt_proxy_get_string_by_pspec (dev, props[PROP_NAME]);
return str;
}
const char *
bolt_device_get_vendor (BoltDevice *dev)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
str = bolt_proxy_get_string_by_pspec (dev, props[PROP_VENDOR]);
return str;
}
guint
bolt_device_get_generation (BoltDevice *dev)
{
guint32 val = 0;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), 0);
val = bolt_proxy_get_uint32_by_pspec (dev, props[PROP_GEN]);
return (guint) val;
}
BoltDeviceType
bolt_device_get_device_type (BoltDevice *dev)
{
gint val = BOLT_DEVICE_PERIPHERAL;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), val);
val = bolt_proxy_get_enum_by_pspec (dev, props[PROP_TYPE]);
return val;
}
gboolean
bolt_device_is_host (BoltDevice *dev)
{
BoltDeviceType dt = bolt_device_get_device_type (dev);
return dt == BOLT_DEVICE_HOST;
}
BoltStatus
bolt_device_get_status (BoltDevice *dev)
{
gint val = BOLT_STATUS_UNKNOWN;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), val);
val = bolt_proxy_get_enum_by_pspec (dev, props[PROP_STATUS]);
return val;
}
BoltAuthFlags
bolt_device_get_authflags (BoltDevice *dev)
{
guint val = BOLT_AUTH_NONE;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), val);
val = bolt_proxy_get_flags_by_pspec (dev, props[PROP_AUTHFLAGS]);
return val;
}
const char *
bolt_device_get_parent (BoltDevice *dev)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
str = bolt_proxy_get_string_by_pspec (dev, props[PROP_PARENT]);
return str;
}
const char *
bolt_device_get_syspath (BoltDevice *dev)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
str = bolt_proxy_get_string_by_pspec (dev, props[PROP_SYSPATH]);
return str;
}
const char *
bolt_device_get_domain (BoltDevice *dev)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
str = bolt_proxy_get_string_by_pspec (dev, props[PROP_DOMAIN]);
return str;
}
guint64
bolt_device_get_conntime (BoltDevice *dev)
{
guint64 val = 0;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), val);
val = bolt_proxy_get_uint64_by_pspec (dev, props[PROP_CONNTIME]);
return val;
}
guint64
bolt_device_get_authtime (BoltDevice *dev)
{
guint64 val = 0;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), val);
val = bolt_proxy_get_uint64_by_pspec (dev, props[PROP_AUTHTIME]);
return val;
}
void
bolt_device_get_linkspeed (BoltDevice *dev,
BoltLinkSpeed *speed)
{
g_auto(GValue) value = G_VALUE_INIT;
BoltLinkSpeed *ls;
gboolean ok;
g_return_if_fail (BOLT_IS_DEVICE (dev));
g_return_if_fail (speed != NULL);
ok = bolt_proxy_get_dbus_property (BOLT_PROXY (dev),
props[PROP_LINKSPEED],
&value);
if (!ok)
return;
ls = g_value_get_boxed (&value);
*speed = *ls;
}
gboolean
bolt_device_is_stored (BoltDevice *dev)
{
gboolean val = FALSE;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), val);
val = bolt_proxy_get_bool_by_pspec (dev, props[PROP_STORED]);
return val;
}
BoltPolicy
bolt_device_get_policy (BoltDevice *dev)
{
gint val = BOLT_POLICY_DEFAULT;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), val);
val = bolt_proxy_get_enum_by_pspec (dev, props[PROP_POLICY]);
return val;
}
BoltKeyState
bolt_device_get_keystate (BoltDevice *dev)
{
gint val = BOLT_KEY_MISSING;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), val);
val = bolt_proxy_get_enum_by_pspec (dev, props[PROP_KEY]);
return val;
}
guint64
bolt_device_get_storetime (BoltDevice *dev)
{
guint64 val = 0;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), val);
val = bolt_proxy_get_uint64_by_pspec (dev, props[PROP_STORETIME]);
return val;
}
const char *
bolt_device_get_label (BoltDevice *dev)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DEVICE (dev), NULL);
str = bolt_proxy_get_string_by_pspec (dev, props[PROP_LABEL]);
return str;
}
char *
bolt_device_get_display_name (BoltDevice *dev)
{
const char *label;
const char *name;
const char *vendor;
label = bolt_device_get_label (dev);
if (label != NULL)
return g_strdup (label);
name = bolt_device_get_name (dev);
vendor = bolt_device_get_vendor (dev);
return g_strdup_printf ("%s %s", vendor, name);
}
guint64
bolt_device_get_timestamp (BoltDevice *dev)
{
BoltStatus status;
guint64 timestamp = 0;
status = bolt_device_get_status (dev);
switch (status)
{
case BOLT_STATUS_AUTHORIZING:
case BOLT_STATUS_AUTH_ERROR:
case BOLT_STATUS_CONNECTING:
case BOLT_STATUS_CONNECTED:
timestamp = bolt_device_get_conntime (dev);
break;
case BOLT_STATUS_DISCONNECTED:
/* implicit: device is stored */
timestamp = bolt_device_get_storetime (dev);
break;
case BOLT_STATUS_AUTHORIZED:
case BOLT_STATUS_AUTHORIZED_DPONLY:
case BOLT_STATUS_AUTHORIZED_NEWKEY:
case BOLT_STATUS_AUTHORIZED_SECURE:
timestamp = bolt_device_get_authtime (dev);
break;
case BOLT_STATUS_UNKNOWN:
timestamp = 0;
break;
}
return timestamp;
}
bolt-0.9.2/cli/bolt-device.h 0000664 0000000 0000000 00000006665 14174530510 0015614 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-proxy.h"
#include "bolt-wire.h"
G_BEGIN_DECLS
#define BOLT_TYPE_DEVICE bolt_device_get_type ()
G_DECLARE_FINAL_TYPE (BoltDevice, bolt_device, BOLT, DEVICE, BoltProxy);
BoltDevice * bolt_device_new_for_object_path (GDBusConnection *bus,
const char *path,
GCancellable *cancellable,
GError **error);
gboolean bolt_device_authorize (BoltDevice *dev,
BoltAuthCtrl flags,
GCancellable *cancellable,
GError **error);
void bolt_device_authorize_async (BoltDevice *dev,
BoltAuthCtrl flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean bolt_device_authorize_finish (BoltDevice *dev,
GAsyncResult *res,
GError **error);
/* getter */
const char * bolt_device_get_uid (BoltDevice *dev);
const char * bolt_device_get_name (BoltDevice *dev);
const char * bolt_device_get_vendor (BoltDevice *dev);
guint bolt_device_get_generation (BoltDevice *dev);
BoltDeviceType bolt_device_get_device_type (BoltDevice *dev);
gboolean bolt_device_is_host (BoltDevice *dev);
BoltStatus bolt_device_get_status (BoltDevice *dev);
BoltAuthFlags bolt_device_get_authflags (BoltDevice *dev);
const char * bolt_device_get_parent (BoltDevice *dev);
const char * bolt_device_get_syspath (BoltDevice *dev);
const char * bolt_device_get_domain (BoltDevice *dev);
guint64 bolt_device_get_conntime (BoltDevice *dev);
guint64 bolt_device_get_authtime (BoltDevice *dev);
void bolt_device_get_linkspeed (BoltDevice *dev,
BoltLinkSpeed *speed);
gboolean bolt_device_is_stored (BoltDevice *dev);
BoltPolicy bolt_device_get_policy (BoltDevice *dev);
BoltKeyState bolt_device_get_keystate (BoltDevice *dev);
guint64 bolt_device_get_storetime (BoltDevice *dev);
const char * bolt_device_get_label (BoltDevice *dev);
/* derived getter */
char * bolt_device_get_display_name (BoltDevice *dev);
guint64 bolt_device_get_timestamp (BoltDevice *dev);
G_END_DECLS
bolt-0.9.2/cli/bolt-domain.c 0000664 0000000 0000000 00000012766 14174530510 0015616 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-domain.h"
#include
struct _BoltDomain
{
BoltProxy parent;
};
enum {
PROP_0,
/* D-Bus Props */
PROP_UID,
PROP_ID,
PROP_SYSPATH,
PROP_SECURITY,
PROP_BOOTACL,
PROP_IOMMU,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = {NULL, };
G_DEFINE_TYPE (BoltDomain,
bolt_domain,
BOLT_TYPE_PROXY);
static void
bolt_domain_class_init (BoltDomainClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = bolt_proxy_property_getter;
gobject_class->set_property = bolt_proxy_property_setter;
props[PROP_UID] =
g_param_spec_string ("uid", "Uid",
"The unique identifier.",
"unknown",
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_ID] =
g_param_spec_string ("id", "Id",
"The sysfs name.",
"unknown",
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_SYSPATH] =
g_param_spec_string ("syspath", "SysfsPath",
"Sysfs path of the udev device.",
"unknown",
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_SECURITY]
= g_param_spec_enum ("security", "SecurityLevel",
"The security level set in the BIOS.",
BOLT_TYPE_SECURITY,
BOLT_SECURITY_UNKNOWN,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_BOOTACL] =
g_param_spec_boxed ("bootacl", "BootACL",
"Pre-boot access control list (uuids).",
G_TYPE_STRV,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
props[PROP_IOMMU] =
g_param_spec_boolean ("iommu", "IOMMU",
"Is IOMMU based DMA protection active?",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
}
static void
bolt_domain_init (BoltDomain *domain)
{
}
/* public methods */
BoltDomain *
bolt_domain_new_for_object_path (GDBusConnection *bus,
const char *path,
GCancellable *cancel,
GError **error)
{
BoltDomain *dom;
g_return_val_if_fail (G_IS_DBUS_CONNECTION (bus), NULL);
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (!cancel || G_IS_CANCELLABLE (cancel), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
dom = g_initable_new (BOLT_TYPE_DOMAIN,
cancel, error,
"g-flags", G_DBUS_PROXY_FLAGS_NONE,
"g-connection", bus,
"g-name", BOLT_DBUS_NAME,
"g-object-path", path,
"g-interface-name", BOLT_DBUS_DOMAIN_INTERFACE,
NULL);
return dom;
}
const char *
bolt_domain_get_uid (BoltDomain *domain)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
str = bolt_proxy_get_string_by_pspec (domain, props[PROP_UID]);
return str;
}
const char *
bolt_domain_get_id (BoltDomain *domain)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
str = bolt_proxy_get_string_by_pspec (domain, props[PROP_ID]);
return str;
}
const char *
bolt_domain_get_syspath (BoltDomain *domain)
{
const char *str;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
str = bolt_proxy_get_string_by_pspec (domain, props[PROP_SYSPATH]);
return str;
}
BoltSecurity
bolt_domain_get_security (BoltDomain *domain)
{
gint val = BOLT_SECURITY_UNKNOWN;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), val);
val = bolt_proxy_get_enum_by_pspec (domain, props[PROP_SECURITY]);
return val;
}
char **
bolt_domain_get_bootacl (BoltDomain *domain)
{
char **strv;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), NULL);
strv = bolt_proxy_get_strv_by_pspec (domain, props[PROP_BOOTACL]);
return strv;
}
gboolean
bolt_domain_is_online (BoltDomain *domain)
{
const char *syspath;
syspath = bolt_domain_get_syspath (domain);
return syspath != NULL;
}
gboolean
bolt_domain_has_iommu (BoltDomain *domain)
{
gboolean val = FALSE;
g_return_val_if_fail (BOLT_IS_DOMAIN (domain), val);
val = bolt_proxy_get_bool_by_pspec (domain, props[PROP_IOMMU]);
return val;
}
bolt-0.9.2/cli/bolt-domain.h 0000664 0000000 0000000 00000003333 14174530510 0015611 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-proxy.h"
G_BEGIN_DECLS
#define BOLT_TYPE_DOMAIN bolt_domain_get_type ()
G_DECLARE_FINAL_TYPE (BoltDomain, bolt_domain, BOLT, DOMAIN, BoltProxy);
BoltDomain * bolt_domain_new_for_object_path (GDBusConnection *bus,
const char *path,
GCancellable *cancellable,
GError **error);
/* getter */
const char * bolt_domain_get_uid (BoltDomain *domain);
const char * bolt_domain_get_id (BoltDomain *domain);
const char * bolt_domain_get_syspath (BoltDomain *domain);
BoltSecurity bolt_domain_get_security (BoltDomain *domain);
char ** bolt_domain_get_bootacl (BoltDomain *domain);
gboolean bolt_domain_is_online (BoltDomain *domain);
gboolean bolt_domain_has_iommu (BoltDomain *domain);
G_END_DECLS
bolt-0.9.2/cli/bolt-power.c 0000664 0000000 0000000 00000014642 14174530510 0015476 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-error.h"
#include "bolt-power.h"
#include
#include
struct _BoltPower
{
BoltProxy parent;
};
enum {
PROP_0,
/* D-Bus Props */
PROP_SUPPORTED,
PROP_STATE,
PROP_TIMEOUT,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = {NULL, };
G_DEFINE_TYPE (BoltPower,
bolt_power,
BOLT_TYPE_PROXY);
static void
bolt_power_class_init (BoltPowerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = bolt_proxy_property_getter;
gobject_class->set_property = bolt_proxy_property_setter;
props[PROP_SUPPORTED] =
g_param_spec_boolean ("supported", "Supported",
"Is forcing power supported?",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_STATE] =
g_param_spec_enum ("state", "State",
"Current force power state.",
BOLT_TYPE_POWER_STATE,
BOLT_FORCE_POWER_UNSET,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
props[PROP_TIMEOUT] =
g_param_spec_uint ("timeout", "Timeout",
"Force power timeout.",
0, G_MAXINT, 0,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
}
static void
bolt_power_init (BoltPower *power)
{
}
/* public methods */
BoltPower *
bolt_power_new_for_object_path (GDBusConnection *bus,
GCancellable *cancel,
GError **error)
{
BoltPower *pwr;
g_return_val_if_fail (G_IS_DBUS_CONNECTION (bus), NULL);
g_return_val_if_fail (!cancel || G_IS_CANCELLABLE (cancel), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
pwr = g_initable_new (BOLT_TYPE_POWER,
cancel, error,
"g-flags", G_DBUS_PROXY_FLAGS_NONE,
"g-connection", bus,
"g-name", BOLT_DBUS_NAME,
"g-object-path", BOLT_DBUS_PATH,
"g-interface-name", BOLT_DBUS_POWER_INTERFACE,
NULL);
return pwr;
}
int
bolt_power_force_power (BoltPower *power,
GError **error)
{
g_autoptr(GUnixFDList) fds = NULL;
g_autoptr(GVariant) val = NULL;
g_autoptr(GError) err = NULL;
GVariant *input;
int fd = -1;
g_return_val_if_fail (BOLT_IS_POWER (power), -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
input = g_variant_new ("(ss)",
"boltctl", /* who */
""); /* flags */
val = g_dbus_proxy_call_with_unix_fd_list_sync (G_DBUS_PROXY (power),
"ForcePower",
input,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&fds,
NULL,
&err);
if (val == NULL)
{
bolt_error_propagate_stripped (error, &err);
return -1;
}
if (g_unix_fd_list_get_length (fds) != 1)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"invalid number of file descriptors returned: %d",
g_unix_fd_list_get_length (fds));
return -1;
}
fd = g_unix_fd_list_get (fds, 0, NULL);
return fd;
}
GPtrArray *
bolt_power_list_guards (BoltPower *power,
GCancellable *cancel,
GError **error)
{
g_autoptr(GVariant) val = NULL;
g_autoptr(GVariantIter) iter = NULL;
GPtrArray *res;
const char *id;
const char *who;
guint pid;
g_return_val_if_fail (BOLT_IS_POWER (power), NULL);
g_return_val_if_fail (!cancel || G_IS_CANCELLABLE (cancel), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
val = g_dbus_proxy_call_sync (G_DBUS_PROXY (power),
"ListGuards",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancel,
error);
if (val == NULL)
return NULL;
res = g_ptr_array_new_with_free_func ((GDestroyNotify) bolt_power_guard_free);
g_variant_get (val, "(a(ssu))", &iter);
while (g_variant_iter_loop (iter, "(ssu)", &id, &who, &pid))
{
BoltPowerGuard *g = g_new (BoltPowerGuard, 1);
g->id = g_strdup (id);
g->who = g_strdup (who);
g->pid = pid;
g_ptr_array_add (res, g);
}
return res;
}
/* getter */
gboolean
bolt_power_is_supported (BoltPower *power)
{
gboolean val;
g_return_val_if_fail (BOLT_IS_POWER (power), FALSE);
val = bolt_proxy_get_bool_by_pspec (power, props[PROP_SUPPORTED]);
return val;
}
BoltPowerState
bolt_power_get_state (BoltPower *power)
{
gint val = BOLT_FORCE_POWER_UNSET;
g_return_val_if_fail (BOLT_IS_POWER (power), val);
val = bolt_proxy_get_enum_by_pspec (power, props[PROP_STATE]);
return val;
}
/* bolt power guard functions */
void
bolt_power_guard_free (BoltPowerGuard *guard)
{
g_return_if_fail (guard != NULL);
g_free (guard->id);
g_free (guard->who);
g_free (guard);
}
bolt-0.9.2/cli/bolt-power.h 0000664 0000000 0000000 00000003504 14174530510 0015476 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-proxy.h"
G_BEGIN_DECLS
#define BOLT_TYPE_POWER bolt_power_get_type ()
G_DECLARE_FINAL_TYPE (BoltPower, bolt_power, BOLT, POWER, BoltProxy);
BoltPower * bolt_power_new_for_object_path (GDBusConnection *bus,
GCancellable *cancellable,
GError **error);
/* methods */
int bolt_power_force_power (BoltPower *power,
GError **error);
GPtrArray * bolt_power_list_guards (BoltPower *power,
GCancellable *cancellable,
GError **error);
/* getter */
gboolean bolt_power_is_supported (BoltPower *power);
BoltPowerState bolt_power_get_state (BoltPower *power);
/* */
typedef struct BoltPowerGuard_
{
char *id;
char *who;
guint pid;
} BoltPowerGuard;
void bolt_power_guard_free (BoltPowerGuard *guard);
G_END_DECLS
bolt-0.9.2/cli/bolt-proxy.c 0000664 0000000 0000000 00000052655 14174530510 0015531 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "bolt-proxy.h"
#include "bolt-dbus.h"
#include "bolt-enums.h"
#include "bolt-error.h"
#include "bolt-glue.h"
#include "bolt-names.h"
#include "bolt-str.h"
static void bolt_proxy_handle_props_changed (GDBusProxy *proxy,
GVariant *changed_properties,
GStrv invalidated_properties,
gpointer user_data);
static void bolt_proxy_handle_dbus_signal (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *params,
gpointer user_data);
enum {
PROP_0,
PROP_BUS,
/* d-bus names */
PROP_OBJECT_PATH,
PROP_IFACE_NAME,
PROP_LAST
};
struct _BoltProxyClassPrivate
{
GHashTable *wire_convs;
};
static gpointer bolt_proxy_parent_class = NULL;
static void bolt_proxy_init (GTypeInstance *,
gpointer g_class);
static void bolt_proxy_class_init (BoltProxyClass *klass);
static void bolt_proxy_base_init (gpointer g_class);
static void bolt_proxy_base_finalize (gpointer g_class);
GType
bolt_proxy_get_type (void)
{
static volatile gsize proxy_type = 0;
if (g_once_init_enter (&proxy_type))
{
GType type_id;
const GTypeInfo type_info = {
sizeof (BoltProxyClass),
bolt_proxy_base_init,
(GBaseFinalizeFunc) bolt_proxy_base_finalize,
(GClassInitFunc) bolt_proxy_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (BoltProxy),
0, /* n_preallocs */
bolt_proxy_init,
NULL, /* value_table */
};
type_id = g_type_register_static (G_TYPE_DBUS_PROXY, "BoltProxy",
&type_info, G_TYPE_FLAG_ABSTRACT);
g_type_add_class_private (type_id, sizeof (BoltProxyClassPrivate));
g_once_init_leave (&proxy_type, type_id);
}
return proxy_type;
}
static void
bolt_proxy_constructed (GObject *object)
{
g_autoptr(GError) err = NULL;
GDBusInterfaceInfo *info;
const char *interface;
G_OBJECT_CLASS (bolt_proxy_parent_class)->constructed (object);
g_signal_connect (object, "g-properties-changed",
G_CALLBACK (bolt_proxy_handle_props_changed), object);
g_signal_connect (object, "g-signal",
G_CALLBACK (bolt_proxy_handle_dbus_signal), object);
if (g_dbus_proxy_get_interface_info (G_DBUS_PROXY (object)) != NULL)
return;
interface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (object));
if (interface == NULL)
return;
bolt_dbus_ensure_resources ();
info = bolt_dbus_interface_info_lookup (BOLT_DBUS_GRESOURCE_PATH,
interface,
&err);
if (info == NULL)
{
g_warning ("could not load interface info: %s", err->message);
return;
}
g_dbus_proxy_set_interface_info (G_DBUS_PROXY (object), info);
g_dbus_interface_info_unref (info);
}
static const BoltProxySignal *
bolt_proxy_get_dbus_signals (guint *n)
{
*n = 0;
return NULL;
}
static void
bolt_proxy_class_init (BoltProxyClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
bolt_proxy_parent_class = g_type_class_peek_parent (klass);
gobject_class->constructed = bolt_proxy_constructed;
klass->get_dbus_signals = bolt_proxy_get_dbus_signals;
}
static void
bolt_proxy_base_init (gpointer g_class)
{
BoltProxyClass *klass = g_class;
klass->priv = G_TYPE_CLASS_GET_PRIVATE (g_class, BOLT_TYPE_PROXY, BoltProxyClassPrivate);
memset (klass->priv, 0, sizeof (BoltProxyClassPrivate));
klass->priv->wire_convs = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify) bolt_wire_conv_unref);
}
static void
bolt_proxy_base_finalize (gpointer g_class)
{
BoltProxyClass *klass = g_class;
BoltProxyClassPrivate *priv = klass->priv;
g_hash_table_unref (priv->wire_convs);
}
static void
bolt_proxy_init (GTypeInstance *instance, gpointer g_class)
{
}
static void
bolt_proxy_handle_props_changed (GDBusProxy *proxy,
GVariant *changed_properties,
GStrv invalidated_properties,
gpointer user_data)
{
g_autoptr(GVariantIter) iter = NULL;
gboolean handled;
GParamSpec **pp;
const char *key;
guint n;
pp = g_object_class_list_properties (G_OBJECT_GET_CLASS (proxy), &n);
g_variant_get (changed_properties, "a{sv}", &iter);
while (g_variant_iter_next (iter, "{&sv}", &key, NULL))
{
handled = FALSE;
for (guint i = 0; !handled && i < n; i++)
{
GParamSpec *pspec = pp[i];
const char *nick;
const char *name;
nick = g_param_spec_get_nick (pspec);
name = g_param_spec_get_name (pspec);
handled = bolt_streq (nick, key);
if (handled)
g_object_notify (G_OBJECT (user_data), name);
}
}
g_free (pp);
}
static void
bolt_proxy_handle_dbus_signal (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *params,
gpointer user_data)
{
const BoltProxySignal *ps;
guint n;
if (signal_name == NULL)
return;
ps = BOLT_PROXY_GET_CLASS (proxy)->get_dbus_signals (&n);
for (guint i = 0; i < n; i++)
{
const BoltProxySignal *sig = &ps[i];
if (g_str_equal (sig->theirs, signal_name))
{
sig->handle (G_OBJECT (proxy), proxy, params);
break;
}
}
}
static BoltWireConv *
bolt_proxy_get_wire_conv (BoltProxy *proxy,
GParamSpec *spec,
GError **error)
{
GDBusInterfaceInfo *info;
GDBusPropertyInfo *pi;
BoltProxyClass *klass;
const char *nick;
BoltWireConv *conv;
klass = BOLT_PROXY_GET_CLASS (proxy);
nick = g_param_spec_get_nick (spec);
conv = g_hash_table_lookup (klass->priv->wire_convs, nick);
if (conv)
return conv;
info = g_dbus_proxy_get_interface_info (G_DBUS_PROXY (proxy));
if (info == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"could not find dbus interface info");
return NULL;
}
pi = g_dbus_interface_info_lookup_property (info, nick);
if (pi == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"could not find dbus property info");
return NULL;
}
conv = bolt_wire_conv_for (G_VARIANT_TYPE (pi->signature), spec);
if (conv == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"could create conversion helper");
return NULL;
}
/* nick is valid as long as spec is valid and
* a reference to spec is held by conv */
g_hash_table_insert (klass->priv->wire_convs,
(gpointer) nick, conv);
return conv;
}
/* public methods */
gboolean
bolt_proxy_set_wireconv (BoltProxy *proxy,
GParamSpec *param_spec,
const char *custom_id,
BoltConvToWire to_wire,
BoltConvFromWire from_wire,
GError **error)
{
GDBusInterfaceInfo *info;
GDBusPropertyInfo *pi;
BoltProxyClass *klass;
const char *nick;
BoltWireConv *conv;
klass = BOLT_PROXY_GET_CLASS (proxy);
nick = g_param_spec_get_nick (param_spec);
conv = g_hash_table_lookup (klass->priv->wire_convs, nick);
if (conv)
return TRUE;
info = g_dbus_proxy_get_interface_info (G_DBUS_PROXY (proxy));
if (info == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"could not find dbus interface info");
return FALSE;
}
pi = g_dbus_interface_info_lookup_property (info, nick);
if (pi == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"could not find dbus property info");
return FALSE;
}
conv = bolt_wire_conv_custom (G_VARIANT_TYPE (pi->signature),
param_spec,
custom_id,
to_wire,
from_wire);
if (conv == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"could create conversion helper");
return FALSE;
}
/* nick is valid as long as spec is valid and
* a reference to spec is held by conv */
g_hash_table_insert (klass->priv->wire_convs,
(gpointer) nick, conv);
return TRUE;
}
void
bolt_proxy_property_getter (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *spec)
{
bolt_proxy_get_dbus_property (BOLT_PROXY (object),
spec,
value);
}
gboolean
bolt_proxy_get_dbus_property (BoltProxy *proxy,
GParamSpec *spec,
GValue *value)
{
g_autoptr(GVariant) val = NULL;
g_autoptr(GError) err = NULL;
BoltWireConv *conv;
const char *nick;
gboolean ok;
nick = g_param_spec_get_nick (spec);
val = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), nick);
if (val == NULL)
{
const GValue *def = g_param_spec_get_default_value (spec);
if (G_VALUE_TYPE (value) == 0)
g_value_init (value, spec->value_type);
g_value_copy (def, value);
g_warning ("Unknown property: %s (%s)", spec->name, nick);
return FALSE;
}
conv = bolt_proxy_get_wire_conv (proxy, spec, &err);
if (conv == NULL)
{
g_warning ("No conversion available for dbus property '%s': %s",
nick, err->message);
return FALSE;
}
ok = bolt_wire_conv_from_wire (conv, val, value, &err);
if (!ok)
g_warning ("Failed to convert dbus property '%s': %s",
nick, err->message);
return ok;
}
const char *
bolt_proxy_get_object_path (BoltProxy *proxy)
{
return g_dbus_proxy_get_object_path (G_DBUS_PROXY (proxy));
}
gboolean
bolt_proxy_has_name_owner (BoltProxy *proxy)
{
const char *name_owner;
g_return_val_if_fail (proxy != NULL, FALSE);
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (proxy));
return name_owner != NULL;
}
static GParamSpec *
find_property (BoltProxy *proxy,
const char *name,
GError **error)
{
GParamSpec *res = NULL;
GParamSpec **pp;
guint n;
pp = g_object_class_list_properties (G_OBJECT_GET_CLASS (proxy), &n);
for (guint i = 0; i < n; i++)
{
GParamSpec *pspec = pp[i];
if (bolt_streq (pspec->name, name))
{
res = pspec;
break;
}
}
if (pp == NULL)
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY,
"could not find property '%s'", name);
g_free (pp);
return res;
}
gboolean
bolt_proxy_get_bool_by_pspec (gpointer object,
GParamSpec *spec)
{
g_auto(GValue) val = G_VALUE_INIT;
BoltProxy *proxy = object;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
bolt_proxy_get_dbus_property (proxy, spec, &val);
return g_value_get_boolean (&val);
}
gint
bolt_proxy_get_enum_by_pspec (gpointer object,
GParamSpec *spec)
{
g_auto(GValue) val = G_VALUE_INIT;
BoltProxy *proxy = (BoltProxy *) object;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
bolt_proxy_get_dbus_property (proxy, spec, &val);
return g_value_get_enum (&val);
}
guint
bolt_proxy_get_flags_by_pspec (gpointer object,
GParamSpec *spec)
{
g_auto(GValue) val = G_VALUE_INIT;
BoltProxy *proxy = (BoltProxy *) object;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
bolt_proxy_get_dbus_property (proxy, spec, &val);
return g_value_get_flags (&val);
}
guint32
bolt_proxy_get_uint32_by_pspec (gpointer object,
GParamSpec *spec)
{
g_auto(GValue) val = G_VALUE_INIT;
BoltProxy *proxy = (BoltProxy *) object;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
bolt_proxy_get_dbus_property (proxy, spec, &val);
return g_value_get_uint (&val);
}
gint64
bolt_proxy_get_int64_by_pspec (gpointer object,
GParamSpec *spec)
{
g_auto(GValue) val = G_VALUE_INIT;
BoltProxy *proxy = (BoltProxy *) object;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
bolt_proxy_get_dbus_property (proxy, spec, &val);
return g_value_get_int64 (&val);
}
guint64
bolt_proxy_get_uint64_by_pspec (gpointer object,
GParamSpec *spec)
{
g_auto(GValue) val = G_VALUE_INIT;
BoltProxy *proxy = (BoltProxy *) object;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
bolt_proxy_get_dbus_property (proxy, spec, &val);
return g_value_get_uint64 (&val);
}
const char *
bolt_proxy_get_string_by_pspec (gpointer object,
GParamSpec *spec)
{
g_auto(GValue) val = G_VALUE_INIT;
BoltProxy *proxy = (BoltProxy *) object;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
bolt_proxy_get_dbus_property (proxy, spec, &val);
/* NB: Yes, this is return the string inside the value,
* although the value is freed. This therefore makes
* the assumption that the GValue's value was initialized
* via a static string (g_value_set_static_string). Ergo,
* this must always be true for bolt_proxy_get_dbus_property
*/
return g_value_get_string (&val);
}
char **
bolt_proxy_get_strv_by_pspec (gpointer object,
GParamSpec *spec)
{
g_auto(GValue) val = G_VALUE_INIT;
BoltProxy *proxy = (BoltProxy *) object;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
bolt_proxy_get_dbus_property (proxy, spec, &val);
return (char **) g_value_dup_boxed (&val);
}
gboolean
bolt_proxy_set_property (BoltProxy *proxy,
const char *name,
GVariant *value,
GCancellable *cancellable,
GError **error)
{
GParamSpec *pp;
const char *iface;
gboolean ok = FALSE;
GVariant *res;
pp = find_property (proxy, name, NULL);
if (pp != NULL)
name = g_param_spec_get_nick (pp);
iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy));
res = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
"org.freedesktop.DBus.Properties.Set",
g_variant_new ("(ssv)",
iface,
name,
value),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (res)
{
g_variant_unref (res);
ok = TRUE;
}
return ok;
}
void
bolt_proxy_set_property_async (BoltProxy *proxy,
const char *name,
GVariant *value,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GParamSpec *pp;
const char *iface;
pp = find_property (proxy, name, NULL);
if (pp != NULL)
name = g_param_spec_get_nick (pp);
iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy));
g_dbus_proxy_call (G_DBUS_PROXY (proxy),
"org.freedesktop.DBus.Properties.Set",
g_variant_new ("(ssv)",
iface,
name,
value),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
callback,
user_data);
}
gboolean
bolt_proxy_set_property_finish (GAsyncResult *res,
GError **error)
{
BoltProxy *proxy;
GVariant *val = NULL;
proxy = (BoltProxy *) g_async_result_get_source_object (res);
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);
if (val == NULL)
return FALSE;
g_variant_unref (val);
return TRUE;
}
gboolean
bolt_proxy_set (BoltProxy *proxy,
GParamSpec *spec,
const GValue *value,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GVariant) val = NULL;
g_autoptr(GVariant) res = NULL;
BoltWireConv *conv;
const char *name;
const char *iface;
g_return_val_if_fail (BOLT_IS_PROXY (proxy), FALSE);
g_return_val_if_fail (G_IS_PARAM_SPEC (spec), FALSE);
g_return_val_if_fail (G_IS_VALUE (value), FALSE);
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
conv = bolt_proxy_get_wire_conv (proxy, spec, error);
if (conv == NULL)
return FALSE;
val = bolt_wire_conv_to_wire (conv, value, error);
if (val == NULL)
return FALSE;
name = g_param_spec_get_nick (spec);
iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy));
res = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
"org.freedesktop.DBus.Properties.Set",
g_variant_new ("(ssv)",
iface,
name,
val),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
return res != NULL;
}
void
bolt_proxy_set_async (BoltProxy *proxy,
GParamSpec *spec,
const GValue *value,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GVariant) val = NULL;
GError *err;
BoltWireConv *conv;
const char *name;
const char *iface;
g_return_if_fail (BOLT_IS_PROXY (proxy));
g_return_if_fail (G_IS_PARAM_SPEC (spec));
g_return_if_fail (G_IS_VALUE (value));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
conv = bolt_proxy_get_wire_conv (proxy, spec, &err);
if (conv != NULL)
val = bolt_wire_conv_to_wire (conv, value, &err);
if (val == NULL)
{
g_task_report_error (proxy, callback, user_data, NULL, err);
return;
}
name = g_param_spec_get_nick (spec);
iface = g_dbus_proxy_get_interface_name (G_DBUS_PROXY (proxy));
g_dbus_proxy_call (G_DBUS_PROXY (proxy),
"org.freedesktop.DBus.Properties.Set",
g_variant_new ("(ssv)",
iface,
name,
val),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
callback,
user_data);
}
gboolean
bolt_proxy_set_finish (GAsyncResult *res,
GError **error)
{
g_autoptr(GVariant) val = NULL;
BoltProxy *proxy;
proxy = (BoltProxy *) g_async_result_get_source_object (res);
val = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);
return val != NULL;
}
static void
set_async_done (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) err = NULL;
g_autoptr(GParamSpec) spec = user_data;
gboolean ok;
ok = bolt_proxy_set_finish (res, &err);
if (!ok)
g_warning ("Error setting property '%s': %s",
spec->name, err->message);
}
void
bolt_proxy_property_setter (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *spec)
{
g_return_if_fail (BOLT_IS_PROXY (object));
g_return_if_fail (value != NULL);
g_return_if_fail (G_IS_PARAM_SPEC (spec));
bolt_proxy_set_async (BOLT_PROXY (object),
spec, value,
NULL,
set_async_done,
g_param_spec_ref (spec));
}
bolt-0.9.2/cli/bolt-proxy.h 0000664 0000000 0000000 00000013044 14174530510 0015523 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include
G_BEGIN_DECLS
typedef struct BoltProxySignal
{
const char *theirs;
void (*handle)(GObject *self,
GDBusProxy *bus_proxy,
GVariant *params);
} BoltProxySignal;
#define BOLT_TYPE_PROXY (bolt_proxy_get_type ())
G_DECLARE_DERIVABLE_TYPE (BoltProxy, bolt_proxy, BOLT, PROXY, GDBusProxy)
typedef struct _BoltProxyClassPrivate BoltProxyClassPrivate;
struct _BoltProxyClass
{
GDBusProxyClass parent;
/*< private >*/
BoltProxyClassPrivate *priv;
/*< public >*/
/* virtuals */
const BoltProxySignal * (*get_dbus_signals) (guint *n);
/* for the future */
gpointer padding[10];
};
gboolean bolt_proxy_set_wireconv (BoltProxy *proxy,
GParamSpec *param_spec,
const char *custom_id,
BoltConvToWire to_wire,
BoltConvFromWire from_wire,
GError **error);
void bolt_proxy_property_getter (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *spec);
gboolean bolt_proxy_get_dbus_property (BoltProxy *proxy,
GParamSpec *spec,
GValue *value);
gboolean bolt_proxy_has_name_owner (BoltProxy *proxy);
const char * bolt_proxy_get_object_path (BoltProxy *proxy)
G_DEPRECATED_FOR (g_dbus_proxy_get_object_path);
gboolean bolt_proxy_get_bool_by_pspec (gpointer proxy,
GParamSpec *spec);
gint bolt_proxy_get_enum_by_pspec (gpointer proxy,
GParamSpec *spec);
guint bolt_proxy_get_flags_by_pspec (gpointer proxy,
GParamSpec *spec);
guint32 bolt_proxy_get_uint32_by_pspec (gpointer proxy,
GParamSpec *spec);
gint64 bolt_proxy_get_int64_by_pspec (gpointer proxy,
GParamSpec *spec);
guint64 bolt_proxy_get_uint64_by_pspec (gpointer proxy,
GParamSpec *spec);
const char * bolt_proxy_get_string_by_pspec (gpointer proxy,
GParamSpec *spec);
char ** bolt_proxy_get_strv_by_pspec (gpointer proxy,
GParamSpec *spec);
gboolean bolt_proxy_set_property (BoltProxy *proxy,
const char *name,
GVariant *value,
GCancellable *cancellable,
GError **error);
void bolt_proxy_set_property_async (BoltProxy *proxy,
const char *name,
GVariant *value,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean bolt_proxy_set_property_finish (GAsyncResult *res,
GError **error);
gboolean bolt_proxy_set (BoltProxy *proxy,
GParamSpec *spec,
const GValue *value,
GCancellable *cancellable,
GError **error);
void bolt_proxy_set_async (BoltProxy *proxy,
GParamSpec *spec,
const GValue *value,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean bolt_proxy_set_finish (GAsyncResult *res,
GError **error);
void bolt_proxy_property_setter (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *spec);
G_END_DECLS
bolt-0.9.2/cli/boltctl-authorize.c 0000664 0000000 0000000 00000011341 14174530510 0017050 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl-cmds.h"
#include "bolt-error.h"
#include "bolt-str.h"
#include
typedef struct AuthorizeAllData
{
GMainLoop *loop;
gboolean done;
gboolean res;
GError **error;
} AuthorizeAllData;
static void
authorize_all_done (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
AuthorizeAllData *data = (AuthorizeAllData *) user_data;
gboolean ok;
ok = bolt_client_authorize_all_finish (BOLT_CLIENT (source),
res,
data->error);
data->res = ok;
data->done = TRUE;
g_main_loop_quit (data->loop);
}
static gboolean
authorize_all (BoltClient *client,
const char *uid,
BoltAuthCtrl flags,
GError **error)
{
g_autoptr(BoltDevice) target = NULL;
g_autoptr(GPtrArray) parents = NULL;
g_autoptr(GPtrArray) uuids = NULL;
g_autoptr(GMainLoop) loop = NULL;
g_autoptr(GError) err = NULL;
AuthorizeAllData data;
target = bolt_client_get_device (client,
uid,
NULL,
&err);
if (target == NULL)
{
g_printerr ("Could not look up target: %s\n",
err->message);
return EXIT_FAILURE;
}
parents = bolt_client_list_parents (client, target, NULL, &err);
if (parents == NULL)
{
g_printerr ("Could not look up parents: %s\n",
err->message);
return EXIT_FAILURE;
}
uuids = g_ptr_array_new_full (parents->len, NULL);
/* reverse order, starting from the root! */
for (guint i = parents->len; i > 0; i--)
{
BoltDevice *dev = g_ptr_array_index (parents, i - 1);
const char *id = bolt_device_get_uid (dev);
BoltStatus status = bolt_device_get_status (dev);
if (!bolt_status_is_pending (status))
continue;
g_ptr_array_add (uuids, (gpointer) id);
}
/* the target itself */
g_ptr_array_add (uuids, (gpointer) uid);
loop = g_main_loop_new (NULL, FALSE);
data.loop = loop;
data.done = FALSE;
data.error = error;
bolt_client_authorize_all_async (client,
uuids,
flags,
NULL,
authorize_all_done,
&data);
if (data.done == FALSE)
g_main_loop_run (loop);
return data.res;
}
int
authorize (BoltClient *client, int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(BoltDevice) dev = NULL;
g_autoptr(GError) error = NULL;
BoltAuthCtrl flags = BOLT_AUTHCTRL_NONE;
BoltStatus status;
const char *uid;
gboolean ok;
gboolean first_time = FALSE;
gboolean chain_arg = FALSE;
GOptionEntry options[] = {
{ "first-time", 'F', 0, G_OPTION_ARG_NONE, &first_time, "Fail if device is already authorized", NULL },
{ "chain", 0, 0, G_OPTION_ARG_NONE, &chain_arg, "Authorize parent devices if necessary" },
{ NULL }
};
optctx = g_option_context_new ("DEVICE - Authorize a device");
g_option_context_add_main_entries (optctx, options, NULL);
if (!g_option_context_parse (optctx, &argc, &argv, &error))
return usage_error (error);
if (argc < 2)
return usage_error_need_arg ("DEVICE");
uid = argv[1];
dev = bolt_client_get_device (client, uid, NULL, &error);
if (dev == NULL)
{
g_printerr ("%s\n", error->message);
return EXIT_FAILURE;
}
status = bolt_device_get_status (dev);
if (chain_arg)
ok = authorize_all (client, uid, flags, &error);
else
ok = bolt_device_authorize (dev, flags, NULL, &error);
if (!ok)
{
if (bolt_err_badstate (error) &&
bolt_status_is_authorized (status) &&
!first_time)
ok = TRUE;
else
g_printerr ("Authorization error: %s\n", error->message);
}
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
bolt-0.9.2/cli/boltctl-cmds.h 0000664 0000000 0000000 00000003520 14174530510 0015771 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "boltctl.h"
G_BEGIN_DECLS
typedef int (*BoltctlCommand) (BoltClient *client,
int argc,
char **argv);
int authorize (BoltClient *client,
int argc,
char **argv);
int config (BoltClient *client,
int argc,
char **argv);
int enroll (BoltClient *client,
int argc,
char **argv);
int forget (BoltClient *client,
int argc,
char **argv);
int info (BoltClient *client,
int argc,
char **argv);
int list_devices (BoltClient *client,
int argc,
char **argv);
int list_domains (BoltClient *client,
int argc,
char **argv);
int monitor (BoltClient *client,
int argc,
char **argv);
int power (BoltClient *client,
int argc,
char **argv);
G_END_DECLS
bolt-0.9.2/cli/boltctl-config.c 0000664 0000000 0000000 00000021357 14174530510 0016313 0 ustar 00root root 0000000 0000000 /*
* Copyright © 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl.h"
#include "boltctl-cmds.h"
#include "bolt-error.h"
#include "bolt-glue.h"
#include "bolt-str.h"
#include
#include
static gboolean
type_for_name (const char *name,
GType *type,
GError **error)
{
if (bolt_strcaseeq (name, "global") ||
bolt_strcaseeq (name, "daemon"))
{
*type = BOLT_TYPE_CLIENT;
return TRUE;
}
if (bolt_strcaseeq (name, "domain"))
{
*type = BOLT_TYPE_DOMAIN;
return TRUE;
}
else if (bolt_strcaseeq (name, "device"))
{
*type = BOLT_TYPE_DEVICE;
return TRUE;
}
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"unknown class '%s'", name);
return FALSE;
}
static const char *
name_for_type (GType type)
{
if (type == BOLT_TYPE_CLIENT)
return "global";
else if (type == BOLT_TYPE_DOMAIN)
return "domain";
else if (type == BOLT_TYPE_DEVICE)
return "device";
else
return "unknown";
}
static BoltProxy *
target_for_type (BoltClient *client,
GType type,
const char *name,
GError **error)
{
BoltProxy *target = NULL;
if (type == BOLT_TYPE_CLIENT)
target = g_object_ref (BOLT_PROXY (client));
else if (type == BOLT_TYPE_DOMAIN)
target = (BoltProxy *) bolt_client_find_domain (client, name, error);
else if (type == BOLT_TYPE_DEVICE)
target = (BoltProxy *) bolt_client_find_device (client, name, error);
else
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"'%s' not handled", name);
return target;
}
static int
property_get (BoltProxy *proxy, GParamSpec *spec, GError **error)
{
g_auto(GValue) prop_val = G_VALUE_INIT;
g_auto(GValue) str_val = G_VALUE_INIT;
g_autofree char *val = NULL;
gboolean ok;
g_value_init (&prop_val, G_PARAM_SPEC_VALUE_TYPE (spec));
g_value_init (&str_val, G_TYPE_STRING);
ok = bolt_proxy_get_dbus_property (proxy, spec, &prop_val);
if (!ok)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"Could not get property");
return EXIT_FAILURE;
}
if (G_IS_PARAM_SPEC_ENUM (spec))
val = g_strdup (bolt_enum_to_string (spec->value_type,
g_value_get_enum (&prop_val),
error));
else if (G_IS_PARAM_SPEC_FLAGS (spec))
val = bolt_flags_to_string (spec->value_type,
g_value_get_flags (&prop_val),
error);
else if (G_IS_PARAM_SPEC_BOOLEAN (spec))
val = g_strdup (bolt_yesno (g_value_get_boolean (&prop_val)));
else if (g_value_transform (&prop_val, &str_val))
val = g_value_dup_string (&str_val);
else
val = g_strdup_value_contents (&prop_val);
if (val == NULL)
return EXIT_FAILURE;
g_print ("%s\n", val);
return EXIT_SUCCESS;
}
static int
property_set (BoltProxy *proxy,
GParamSpec *spec,
const char *str,
GError **error)
{
g_auto(GValue) val = G_VALUE_INIT;
gboolean ok;
ok = bolt_str_parse_by_pspec (spec, str, &val, error);
if (!ok)
return EXIT_FAILURE;
ok = bolt_proxy_set (proxy, spec, &val, NULL, error);
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* describe */
static void
describe_properties (GType type)
{
g_autoptr(GPtrArray) props = NULL;
const char *prefix = name_for_type (type);
props = bolt_properties_for_type (type);
for (guint i = 0; i < props->len; i++)
{
GParamSpec *spec = g_ptr_array_index (props, i);
const char *desc = g_param_spec_get_blurb (spec);
gboolean can_write = spec->flags & G_PARAM_WRITABLE;
const char *rw = can_write ? "rw" : "r-";
g_print ("%s %s.%-14s %s\n", rw, prefix, spec->name, desc);
}
}
static int
do_describe (BoltClient *client, int argc, char **argv)
{
g_autoptr(GError) err = NULL;
gboolean ok;
ok = check_argc (argc, 0, 1, &err);
if (!ok)
return usage_error (err);
if (argc == 1) /* boltctl config --describe */
{
describe_properties (BOLT_TYPE_CLIENT);
describe_properties (BOLT_TYPE_DOMAIN);
describe_properties (BOLT_TYPE_DEVICE);
}
else if (argc == 2) /* boltctl config --describe {global,device,domain} */
{
GType type;
ok = type_for_name (argv[1], &type, &err);
if (!ok)
return usage_error (err);
describe_properties (type);
}
return 0;
}
static gboolean
parse_option (const char *opt_str,
GType *type_out,
char **prop_out,
GError **error)
{
g_auto(GStrv) comps = NULL;
GType type = 0;
gboolean ok;
guint n;
comps = g_strsplit (opt_str, ".", 2);
n = g_strv_length (comps);
*prop_out = NULL;
/* Determine the fine structure of OPTION, i.e. is it
* type.property or property */
switch (n)
{
case 1:
type = BOLT_TYPE_CLIENT;
*prop_out = g_steal_pointer (&comps[0]);
ok = TRUE;
break;
case 2:
ok = type_for_name (comps[0], &type, error);
*prop_out = g_steal_pointer (&comps[1]);
break;
default:
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"invalid OPTION string '%s'", opt_str);
ok = FALSE;
break;
}
*type_out = type;
return ok;
}
/* */
typedef enum {
ACTION_GET = 1,
ACTION_SET = 2,
} Action;
static const char *summary =
"Describing available items: \n"
" config --describe [global|domain|device]\n"
"\n"
"Getting items:\n"
" config KEY\n"
" config .KEY TARGET\n"
"\n"
"Setting items:\n"
" config KEY VALUE\n"
" config .KEY TARGET VALUE\n";
int
config (BoltClient *client, int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(GPtrArray) props = NULL;
g_autoptr(BoltProxy) target = NULL;
g_autoptr(GError) err = NULL;
g_autofree char *pstr = NULL;
const char *value = NULL;
gboolean describe = FALSE;
gboolean ok = FALSE;
GParamSpec *prop;
Action action;
GType type = 0;
int res = EXIT_FAILURE;
GOptionEntry options[] = {
{ "describe", 'd', 0, G_OPTION_ARG_NONE, &describe, "Describe options", NULL },
{ NULL }
};
optctx = g_option_context_new ("- Inspect and modify options");
g_option_context_add_main_entries (optctx, options, NULL);
g_option_context_set_summary (optctx, summary);
g_option_context_set_strict_posix (optctx, TRUE);
if (!g_option_context_parse (optctx, &argc, &argv, &err))
return usage_error (err);
if (describe)
return do_describe (client, argc, argv);
/* get or set */
ok = check_argc (argc, 1, 3, &err);
if (!ok)
return usage_error (err);
ok = parse_option (argv[1], &type, &pstr, &err);
if (!ok)
return usage_error (err);
props = bolt_properties_for_type (type);
ok = bolt_properties_find (props, pstr, &prop, &err);
if (!ok)
return usage_error (err);
if (argc == 2)
{
/* boltctl config */
target = g_object_ref (BOLT_PROXY (client));
action = ACTION_GET;
}
else if (argc == 3 && type != BOLT_TYPE_CLIENT)
{
/* boltctl config <{device, domain}.property> */
target = target_for_type (client, type, argv[2], &err);
action = ACTION_GET;
}
else if (argc == 3 && type == BOLT_TYPE_CLIENT)
{
/* boltctl config */
target = g_object_ref (BOLT_PROXY (client));
action = ACTION_SET;
value = argv[2];
}
else if (argc == 4)
{
/* boltctl config <{device, domain}.property> */
target = target_for_type (client, type, argv[2], &err);
action = ACTION_SET;
value = argv[3];
}
if (target == NULL)
return usage_error (err); /* must be set if target == NULL */
/* action must either be GET or SET */
if (action == ACTION_GET)
res = property_get (target, prop, &err);
else if (action == ACTION_SET)
res = property_set (target, prop, value, &err);
if (res == EXIT_FAILURE)
report_error (NULL, err);
return res;
}
bolt-0.9.2/cli/boltctl-domains.c 0000664 0000000 0000000 00000010366 14174530510 0016476 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl-cmds.h"
#include "boltctl-uidfmt.h"
#include "bolt-str.h"
#include
/* domain related commands */
static void
print_domain (BoltDomain *domain, gboolean verbose)
{
g_auto(GStrv) bootacl = NULL;
const char *tree_branch;
const char *tree_right;
const char *tree_cont;
const char *tree_space;
const char *uid;
const char *name;
const char *syspath;
const char *security;
BoltSecurity sl;
gboolean online;
gboolean iommu;
tree_branch = bolt_glyph (TREE_BRANCH);
tree_right = bolt_glyph (TREE_RIGHT);
tree_cont = bolt_glyph (TREE_VERTICAL);
tree_space = bolt_glyph (TREE_SPACE);
uid = bolt_domain_get_uid (domain);
name = bolt_domain_get_id (domain);
sl = bolt_domain_get_security (domain);
syspath = bolt_domain_get_syspath (domain);
security = bolt_security_to_string (sl);
bootacl = bolt_domain_get_bootacl (domain);
online = syspath != NULL;
if (online)
{
g_print (" %s%s%s ",
bolt_color (ANSI_GREEN),
bolt_glyph (BLACK_CIRCLE),
bolt_color (ANSI_NORMAL));
}
else
{
g_print (" %s ", bolt_glyph (WHITE_CIRCLE));
}
g_print ("%s %s", (name ? : "domain"), format_uid (uid));
g_print ("\n");
if (verbose)
g_print (" %s online: %s\n", tree_branch, bolt_yesno (online));
if (verbose && syspath != NULL)
g_print (" %s syspath: %s\n", tree_branch, syspath);
if (bootacl)
{
guint acl_max = g_strv_length ((char **) bootacl);
guint used = 0;
guint i;
for (i = 0; i < acl_max; i++)
if (!bolt_strzero (bootacl[i]))
used++;
g_print (" %s bootacl: %u/%u\n", tree_branch, used, acl_max);
for (i = 0; i < acl_max; i++)
{
const char *tree_sym = used > 1 ? tree_branch : tree_right;
if (bolt_strzero (bootacl[i]))
continue;
g_print (" %s %s[%u]", tree_cont, tree_sym, i);
g_print (" %s\n", format_uid (bootacl[i]));
used--;
}
}
iommu = bolt_domain_has_iommu (domain);
g_print (" %s security: ", tree_right);
if (bolt_security_is_interactive (sl) && iommu)
g_print ("iommu+%s", security);
else if (bolt_security_allows_pcie (sl) && iommu)
g_print ("iommu");
else
g_print ("%s", security);
g_print ("\n");
if (verbose)
{
g_print (" %s %s iommu: %s\n", tree_space, tree_branch,
bolt_yesno (iommu));
g_print (" %s %s level: %s\n", tree_space, tree_right,
security);
}
g_print ("\n");
}
int
list_domains (BoltClient *client, int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(GError) err = NULL;
g_autoptr(GPtrArray) domains = NULL;
gboolean details = FALSE;
GOptionEntry options[] = {
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &details, "Show more details", NULL },
{ NULL }
};
optctx = g_option_context_new ("- List thunderbolt domains");
g_option_context_add_main_entries (optctx, options, NULL);
if (!g_option_context_parse (optctx, &argc, &argv, &err))
return usage_error (err);
domains = bolt_client_list_domains (client, NULL, &err);
if (domains == NULL)
{
g_warning ("Could not list domains: %s", err->message);
domains = g_ptr_array_new_with_free_func (g_object_unref);
}
for (guint i = 0; i < domains->len; i++)
{
BoltDomain *dom = g_ptr_array_index (domains, i);
print_domain (dom, details);
}
return EXIT_SUCCESS;
}
bolt-0.9.2/cli/boltctl-enroll.c 0000664 0000000 0000000 00000011234 14174530510 0016332 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl-cmds.h"
#include "bolt-str.h"
#include
typedef struct EnrollAllData
{
GMainLoop *loop;
gboolean done;
gboolean res;
} EnrollAllData;
static void
enroll_all_done (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) err = NULL;
EnrollAllData *data = (EnrollAllData *) user_data;
gboolean ok;
ok = bolt_client_enroll_all_finish (BOLT_CLIENT (source), res, &err);
if (!ok)
g_printerr ("Could not enroll all devices: %s\n", err->message);
data->done = TRUE;
data->res = ok;
g_main_loop_quit (data->loop);
}
static int
enroll_all (BoltClient *client,
const char *uid,
BoltPolicy policy,
BoltAuthCtrl flags)
{
g_autoptr(BoltDevice) target = NULL;
g_autoptr(GPtrArray) parents = NULL;
g_autoptr(GPtrArray) uuids = NULL;
g_autoptr(GMainLoop) loop = NULL;
g_autoptr(GError) err = NULL;
EnrollAllData data;
target = bolt_client_get_device (client,
uid,
NULL,
&err);
if (target == NULL)
{
g_printerr ("Could not look up target: %s\n",
err->message);
return EXIT_FAILURE;
}
parents = bolt_client_list_parents (client, target, NULL, &err);
if (parents == NULL)
{
g_printerr ("Could not look up parents: %s\n",
err->message);
return EXIT_FAILURE;
}
uuids = g_ptr_array_new_full (parents->len, NULL);
/* reverse order, starting from the root! */
for (guint i = parents->len; i > 0; i--)
{
BoltDevice *dev = g_ptr_array_index (parents, i - 1);
const char *id = bolt_device_get_uid (dev);
if (bolt_device_is_stored (dev) ||
bolt_device_is_host (dev))
continue;
g_ptr_array_add (uuids, (gpointer) id);
}
/* the target itself */
g_ptr_array_add (uuids, (gpointer) uid);
loop = g_main_loop_new (NULL, FALSE);
data.loop = loop;
data.done = FALSE;
bolt_client_enroll_all_async (client,
uuids,
policy,
flags,
NULL,
enroll_all_done,
&data);
if (data.done == FALSE)
g_main_loop_run (loop);
return data.res ? EXIT_SUCCESS : EXIT_FAILURE;
}
int
enroll (BoltClient *client, int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(BoltDevice) dev = NULL;
g_autoptr(GError) error = NULL;
const char *uid;
BoltPolicy policy = BOLT_POLICY_DEFAULT;
BoltAuthCtrl flags = BOLT_AUTHCTRL_NONE;
const char *policy_arg = "default";
gboolean chain_arg = FALSE;
GOptionEntry options[] = {
{ "policy", 0, 0, G_OPTION_ARG_STRING, &policy_arg, "Policy for the device; one of {auto, manual, *default}", "POLICY" },
{ "chain", 0, 0, G_OPTION_ARG_NONE, &chain_arg, "Authorize parent devices if necessary" },
{ NULL }
};
optctx = g_option_context_new ("DEVICE - Authorize and store a device in the database");
g_option_context_add_main_entries (optctx, options, NULL);
if (!g_option_context_parse (optctx, &argc, &argv, &error))
return usage_error (error);
if (argc < 2)
return usage_error_need_arg ("DEVICE");
policy = bolt_policy_from_string (policy_arg);
if (!bolt_policy_validate (policy))
{
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"invalid policy '%s'", policy_arg);
return usage_error (error);
}
uid = argv[1];
if (chain_arg)
return enroll_all (client, uid, policy, flags);
dev = bolt_client_enroll_device (client, uid, policy, flags, &error);
if (dev == NULL)
{
g_printerr ("%s\n", error->message);
return EXIT_FAILURE;
}
print_device (dev, TRUE);
return EXIT_SUCCESS;
}
bolt-0.9.2/cli/boltctl-forget.c 0000664 0000000 0000000 00000005077 14174530510 0016335 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl-cmds.h"
#include "bolt-str.h"
#include
int
forget (BoltClient *client, int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(GError) error = NULL;
const char *uid;
gboolean ok, forget_all = FALSE;
GOptionEntry options[] = {
{ "all", 'a', 0, G_OPTION_ARG_NONE, &forget_all, "Forget all devices", NULL },
{ NULL }
};
optctx = g_option_context_new ("DEVICE - Remove a device from the store");
g_option_context_add_main_entries (optctx, options, NULL);
if (!g_option_context_parse (optctx, &argc, &argv, &error))
return usage_error (error);
if (forget_all)
{
g_autoptr(GPtrArray) devices = NULL;
ok = check_argc (argc, 0, 0, &error);
if (!ok)
return usage_error (error);
devices = bolt_client_list_devices (client, NULL, &error);
if (devices == NULL)
{
g_printerr ("Failed to list devices: %s",
error->message);
return EXIT_FAILURE;
}
for (guint i = 0; i < devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (devices, i);
if (!bolt_device_is_stored (dev))
continue;
uid = bolt_device_get_uid (dev);
ok = bolt_client_forget_device (client, uid, &error);
if (!ok)
{
g_printerr ("Failed to forget device: %s\n", error->message);
return EXIT_FAILURE;
}
}
}
else
{
if (argc < 2)
return usage_error_need_arg ("DEVICE");
uid = argv[1];
ok = bolt_client_forget_device (client, uid, &error);
if (!ok)
{
g_printerr ("Failed to forget device: %s\n", error->message);
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
bolt-0.9.2/cli/boltctl-info.c 0000664 0000000 0000000 00000002771 14174530510 0016000 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl-cmds.h"
#include "bolt-str.h"
#include
int
info (BoltClient *client, int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(BoltDevice) dev = NULL;
g_autoptr(GError) error = NULL;
const char *uid;
optctx = g_option_context_new ("DEVICE - Show information about a device");
if (!g_option_context_parse (optctx, &argc, &argv, &error))
return usage_error (error);
if (argc < 2)
return usage_error_need_arg ("DEVICE");
uid = argv[1];
dev = bolt_client_get_device (client, uid, NULL, &error);
if (dev == NULL)
{
g_printerr ("%s\n", error->message);
return EXIT_FAILURE;
}
print_device (dev, TRUE);
return EXIT_SUCCESS;
}
bolt-0.9.2/cli/boltctl-list.c 0000664 0000000 0000000 00000003775 14174530510 0016025 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl-cmds.h"
#include "bolt-str.h"
#include
int
list_devices (BoltClient *client, int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
gboolean show_all = FALSE;
GOptionEntry options[] = {
{ "all", 'a', 0, G_OPTION_ARG_NONE, &show_all, "Show all devices", NULL },
{ NULL }
};
optctx = g_option_context_new ("- List thunderbolt devices");
g_option_context_add_main_entries (optctx, options, NULL);
if (!g_option_context_parse (optctx, &argc, &argv, &error))
return usage_error (error);
devices = bolt_client_list_devices (client, NULL, &error);
if (devices == NULL)
{
g_printerr ("Failed to list devices: %s",
error->message);
return EXIT_FAILURE;
}
bolt_devices_sort_by_syspath (devices, FALSE);
for (guint i = 0; i < devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (devices, i);
BoltDeviceType type;
if (!show_all)
{
type = bolt_device_get_device_type (dev);
if (type != BOLT_DEVICE_PERIPHERAL)
continue;
}
print_device (dev, FALSE);
}
return EXIT_SUCCESS;
}
bolt-0.9.2/cli/boltctl-monitor.c 0000664 0000000 0000000 00000020066 14174530510 0016531 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl-cmds.h"
#include "bolt-str.h"
#include
static void
handle_domain_added (BoltClient *cli,
const char *opath,
gpointer user_data)
{
g_autoptr(GError) err = NULL;
GDBusConnection *bus;
BoltDomain *dom;
GPtrArray *domains = user_data;
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (cli));
dom = bolt_domain_new_for_object_path (bus, opath, NULL, &err);
if (err != NULL)
{
g_warning ("Could not create proxy object for %s", opath);
return;
}
g_print (" DomainAdded: %s\n", opath);
g_ptr_array_add (domains, dom);
}
static void
handle_domain_removed (BoltClient *cli,
const char *opath,
gpointer user_data)
{
GPtrArray *domains = user_data;
BoltDomain *domain = NULL;
for (guint i = 0; i < domains->len; i++)
{
BoltDomain *dom = g_ptr_array_index (domains, i);
const char *dom_opath = g_dbus_proxy_get_object_path (G_DBUS_PROXY (dom));
if (bolt_streq (opath, dom_opath))
{
domain = dom;
break;
}
}
if (domain == NULL)
{
g_warning ("DomainRemoved signal for unknown domain: %s", opath);
return;
}
g_print (" DomainRemoved: %s\n", opath);
g_ptr_array_remove_fast (domains, domain);
}
static void
handle_device_changed (GObject *gobject,
GParamSpec *pspec,
gpointer user_data)
{
const char *uid = NULL;
const char *dev_name = NULL;
g_autofree char *val = NULL;
BoltDevice *dev = BOLT_DEVICE (gobject);
const char *prop_name;
GValue prop_val = G_VALUE_INIT;
GValue str_val = G_VALUE_INIT;
uid = bolt_device_get_uid (dev);
dev_name = bolt_device_get_name (dev);
prop_name = g_param_spec_get_name (pspec);
g_value_init (&prop_val, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_value_init (&str_val, G_TYPE_STRING);
g_print ("%" G_GINT64_FORMAT " ", g_get_real_time ());
g_object_get_property (G_OBJECT (dev), prop_name, &prop_val);
g_print ("[%s] %30s | %10s -> ", uid, dev_name, prop_name);
if (g_value_transform (&prop_val, &str_val))
val = g_value_dup_string (&str_val);
else
val = g_strdup_value_contents (&prop_val);
g_print ("%s\n", val ? : "");
g_value_unset (&str_val);
g_value_unset (&prop_val);
}
static void
handle_device_added (BoltClient *cli,
const char *opath,
gpointer user_data)
{
g_autoptr(GError) err = NULL;
GDBusConnection *bus;
BoltDevice *dev;
GPtrArray *devices = user_data;
const char *name;
const char *uid;
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (cli));
dev = bolt_device_new_for_object_path (bus, opath, NULL, &err);
if (err != NULL)
{
g_warning ("Could not create proxy object for %s", opath);
return;
}
uid = bolt_device_get_uid (dev);
name = bolt_device_get_name (dev);
g_print ("%" G_GINT64_FORMAT " ", g_get_real_time ());
g_print ("[%s] %30s | DeviceAdded @ %s\n", uid, name, opath);
g_ptr_array_add (devices, dev);
g_signal_connect (dev, "notify",
G_CALLBACK (handle_device_changed),
NULL);
}
static void
handle_device_removed (BoltClient *cli,
const char *opath,
gpointer user_data)
{
GPtrArray *devices = user_data;
BoltDevice *device = NULL;
const char *name;
const char *uid;
for (guint i = 0; i < devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (devices, i);
const char *dev_opath = g_dbus_proxy_get_object_path (G_DBUS_PROXY (dev));
if (bolt_streq (opath, dev_opath))
{
device = dev;
break;
}
}
if (device == NULL)
{
g_warning ("DeviceRemoved signal for unknown device: %s", opath);
return;
}
uid = bolt_device_get_uid (device);
name = bolt_device_get_name (device);
g_print ("%" G_GINT64_FORMAT " ", g_get_real_time ());
g_print ("[%s] %30s | DeviceRemoved @ %s\n", uid, name, opath);
g_signal_handlers_block_by_func (device, handle_device_changed, devices);
g_ptr_array_remove_fast (devices, device);
}
static void
handle_probing_changed (BoltClient *client,
GParamSpec *pspec,
gpointer user_data)
{
gboolean probing = bolt_client_is_probing (client);
g_print ("%" G_GINT64_FORMAT " ", g_get_real_time ());
if (probing)
g_print ("Probing started\n");
else
g_print ("Probing done\n");
}
int
monitor (BoltClient *client, int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GMainLoop) main_loop = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) domains = NULL;
g_autofree char *amstr = NULL;
BoltSecurity security;
BoltAuthMode authmode;
guint version = 0;
optctx = g_option_context_new ("- Watch for changes");
if (!g_option_context_parse (optctx, &argc, &argv, &error))
return usage_error (error);
version = bolt_client_get_version (client);
security = bolt_client_get_security (client);
authmode = bolt_client_get_authmode (client);
amstr = bolt_flags_to_string (BOLT_TYPE_AUTH_MODE, authmode, NULL);
if (!bolt_proxy_has_name_owner (BOLT_PROXY (client)))
g_print ("%s no name owner for bolt (not running?)\n",
bolt_glyph (WARNING_SIGN));
g_print ("Bolt Version : %d.%d\n", VERSION_MAJOR, VERSION_MINOR);
g_print ("Daemon API : %u\n", version);
g_print ("Client API : %u\n", BOLT_DBUS_API_VERSION);
g_print ("Security Level: %s\n", bolt_security_to_string (security));
g_print ("Auth Mode : %s\n", amstr);
g_print ("Ready\n");
/* domains */
domains = bolt_client_list_domains (client, NULL, &error);
if (domains == NULL)
{
g_warning ("Could not list domains: %s", error->message);
domains = g_ptr_array_new_with_free_func (g_object_unref);
g_clear_error (&error);
}
g_signal_connect (client, "domain-added",
G_CALLBACK (handle_domain_added), domains);
g_signal_connect (client, "domain-removed",
G_CALLBACK (handle_domain_removed), domains);
/* devices */
devices = bolt_client_list_devices (client, NULL, &error);
if (devices == NULL)
{
g_warning ("Could not list devices: %s", error->message);
devices = g_ptr_array_new_with_free_func (g_object_unref);
g_clear_error (&error);
}
bolt_devices_sort_by_syspath (devices, FALSE);
for (guint i = 0; i < devices->len; i++)
{
BoltDevice *dev = g_ptr_array_index (devices, i);
g_signal_connect (dev, "notify",
G_CALLBACK (handle_device_changed),
NULL);
}
g_signal_connect (client, "device-added",
G_CALLBACK (handle_device_added), devices);
g_signal_connect (client, "device-removed",
G_CALLBACK (handle_device_removed), devices);
g_signal_connect (client, "notify::probing",
G_CALLBACK (handle_probing_changed), NULL);
main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (main_loop);
g_signal_handlers_disconnect_by_func (client,
G_CALLBACK (handle_probing_changed),
NULL);
return EXIT_SUCCESS;
}
bolt-0.9.2/cli/boltctl-power.c 0000664 0000000 0000000 00000007115 14174530510 0016176 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl-cmds.h"
#include "bolt-str.h"
#include
static gboolean
quit_main_loop (gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return FALSE;
}
int
power (BoltClient *client, int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(GMainLoop) main_loop = NULL;
g_autoptr(BoltPower) power = NULL;
g_autoptr(GError) error = NULL;
BoltPowerState state;
gboolean do_query = FALSE;
int fd;
double timeout = 0;
GOptionEntry options[] = {
{ "query", 'q', 0, G_OPTION_ARG_NONE, &do_query, "Query the status", NULL },
{ "timeout", 't', 0, G_OPTION_ARG_DOUBLE, &timeout, "Quit after N seconds", NULL },
{ NULL }
};
optctx = g_option_context_new ("- Force power configuration");
g_option_context_add_main_entries (optctx, options, NULL);
if (!g_option_context_parse (optctx, &argc, &argv, &error))
return usage_error (error);
power = bolt_client_new_power_client (client, NULL, &error);
if (power == NULL)
{
g_warning ("Could get proxy for power interface: %s",
error->message);
return EXIT_FAILURE;
}
if (do_query)
{
g_autoptr(GPtrArray) guards = NULL;
const char *tree_branch;
const char *tree_right;
const char *str;
gboolean supported;
supported = bolt_power_is_supported (power);
g_print ("supported: %s\n", bolt_yesno (supported));
if (!supported)
return EXIT_SUCCESS;
state = bolt_power_get_state (power);
str = bolt_power_state_to_string (state);
g_print ("power state: %s\n", str);
guards = bolt_power_list_guards (power, NULL, &error);
if (guards == NULL)
{
g_warning ("Could not list guards: %s", error->message);
return EXIT_FAILURE;
}
tree_branch = bolt_glyph (TREE_BRANCH);
tree_right = bolt_glyph (TREE_RIGHT);
g_print ("%u active power guards%s\n", guards->len,
guards->len > 0 ? ":" : "");
for (guint i = 0; i < guards->len; i++)
{
BoltPowerGuard *g = g_ptr_array_index (guards, 0);
g_print (" guard '%s'\n", g->id);
g_print (" %s who: %s\n", tree_branch, g->who);
g_print (" %s pid: %u\n", tree_right, g->pid);
g_print ("\n");
}
return EXIT_SUCCESS;
}
fd = bolt_power_force_power (power, &error);
if (fd == -1)
{
g_warning ("Could force power controller: %s", error->message);
return EXIT_SUCCESS;
}
g_print ("acquired power guard (%d)\n", fd);
main_loop = g_main_loop_new (NULL, FALSE);
if (timeout > 0.)
{
guint tout = (guint) timeout * 1000;
g_timeout_add (tout, quit_main_loop, main_loop);
}
g_main_loop_run (main_loop);
(void) close (fd);
return EXIT_SUCCESS;
}
bolt-0.9.2/cli/boltctl-uidfmt.c 0000664 0000000 0000000 00000011273 14174530510 0016332 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-str.h"
#include "bolt-term.h"
#include "boltctl-uidfmt.h"
#include
#include
#include
char *
bolt_uuid_format (const char *uuid,
const char *salt,
int fmt)
{
g_autoptr(GChecksum) chk = NULL;
const char *tmp = NULL;
guint op;
int len;
char *res;
if (uuid == NULL)
return NULL;
g_return_val_if_fail (fmt > -1, NULL);
op = fmt & 0xFF;
switch (op)
{
case BOLT_UID_FORMAT_FULL:
res = g_strdup (uuid);
break;
case BOLT_UID_FORMAT_SHORT:
res = g_strdup_printf ("%.13s%s", uuid, bolt_glyph (ELLIPSIS));
break;
case BOLT_UID_FORMAT_ALIAS:
chk = g_checksum_new (G_CHECKSUM_SHA1);
if (salt)
g_checksum_update (chk, (const guchar *) salt, -1);
g_checksum_update (chk, (const guchar *) uuid, -1);
tmp = g_checksum_get_string (chk);
res = g_strdup_printf ("%.8s-%.4s-%.4s-%.4s-%.12s",
tmp, tmp + 8, tmp + 12, tmp + 16, tmp + 20);
break;
case BOLT_UID_FORMAT_LEN:
len = MIN (fmt >> 8, 36);
res = g_strdup_printf ("%.*s%s", len, uuid,
(len < 36 ? bolt_glyph (ELLIPSIS) : ""));
break;
default:
res = NULL;
g_warning ("unknown uuid format enum value: %d", fmt);
}
return res;
}
int
bolt_uuid_format_from_string (const char *str,
GError **error)
{
char *end;
gint64 val;
if (bolt_streq (str, "short"))
return BOLT_UID_FORMAT_SHORT;
else if (bolt_streq (str, "full"))
return BOLT_UID_FORMAT_FULL;
else if (bolt_streq (str, "alias"))
return BOLT_UID_FORMAT_ALIAS;
val = g_ascii_strtoll (str, &end, 10);
if (str == end)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"uuid format: unknown style: %s", str);
return -1;
}
else if (val < 1)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"uuid format: invalid number: %s", str);
return -1;
}
else if (val > 36)
{
return BOLT_UID_FORMAT_FULL;
}
return BOLT_UID_FORMAT_LEN | (val << 8);
}
/* */
int uuids_format;
GHashTable *uuids_table;
gboolean uuids_cleanup;
char *uuids_salt;
static void
format_uid_cleanup (void)
{
g_clear_pointer (&uuids_table, g_hash_table_unref);
g_clear_pointer (&uuids_salt, g_free);
uuids_format = 0;
}
#define MACHINE_ID_PATH "/etc/machine-id"
#define BOOT_ID_PATH "/proc/sys/kernel/random/boot_id"
static char *
get_salt (void)
{
char *salt = NULL;
gboolean ok;
ok = g_file_get_contents (MACHINE_ID_PATH, &salt, NULL, NULL);
if (ok && !bolt_strzero (salt))
{
g_debug ("using machine-id as salt");
return salt;
}
ok = g_file_get_contents (BOOT_ID_PATH, &salt, NULL, NULL);
if (ok && !bolt_strzero (salt))
{
g_debug ("using boot-id as salt");
return salt;
}
g_debug ("using PACKAGE_VERSION as pseudo-salt :(");
return g_strdup (PACKAGE_VERSION);
}
int
format_uid_init (const char *str,
GError **error)
{
int fmt;
g_return_val_if_fail (str != NULL, -1);
fmt = bolt_uuid_format_from_string (str, error);
if (fmt < 0)
return fmt;
uuids_format = fmt;
if (uuids_table)
g_hash_table_unref (uuids_table);
uuids_table = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_free);
uuids_salt = get_salt ();
if (!uuids_cleanup)
{
uuids_cleanup = TRUE;
atexit (format_uid_cleanup);
}
return fmt;
}
const char *
format_uid (const char *uid)
{
const char *res;
char *key, *val;
if (uid == NULL)
uid = "";
res = g_hash_table_lookup (uuids_table, uid);
if (res != NULL)
return res;
key = g_strdup (uid);
val = bolt_uuid_format (uid, uuids_salt, uuids_format);
g_hash_table_insert (uuids_table, key, val);
return val;
}
bolt-0.9.2/cli/boltctl-uidfmt.h 0000664 0000000 0000000 00000002702 14174530510 0016334 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
/* how to format uuids */
typedef enum {
BOLT_UID_FORMAT_FULL = 0,
BOLT_UID_FORMAT_SHORT = 1,
BOLT_UID_FORMAT_ALIAS = 2,
BOLT_UID_FORMAT_LEN = 0xFF,
} BoltUuidFormat;
/* generic function */
char * bolt_uuid_format (const char *uuid,
const char *salt,
int fmt);
int bolt_uuid_format_from_string (const char *str,
GError **error);
/* */
int format_uid_init (const char *str,
GError **error);
const char * format_uid (const char *uid);
G_END_DECLS
bolt-0.9.2/cli/boltctl.c 0000664 0000000 0000000 00000032705 14174530510 0015047 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "boltctl-cmds.h"
#include "boltctl-uidfmt.h"
#include "bolt-client.h"
#include "bolt-enums.h"
#include "bolt-error.h"
#include "bolt-macros.h"
#include "bolt-str.h"
#include "bolt-term.h"
#include "bolt-time.h"
#include
#include
#include
gboolean
check_argc (int argc,
int lower,
int upper,
GError **error)
{
argc--; /* discard argv[0] */
if (lower == upper && argc != upper)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"unexpected number of arguments: %d, wanted %d",
argc, upper);
return FALSE;
}
else if (argc < lower)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"not enough arguments: %d, wanted at least %d",
argc, lower);
return FALSE;
}
else if (argc > upper)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"too many arguments: %d, wanted at most %d",
argc, lower);
return FALSE;
}
return TRUE;
}
int
usage_error (GError *error)
{
g_printerr ("%s:", g_get_application_name ());
g_printerr ("%s error: %s", bolt_color (ANSI_RED), bolt_color (ANSI_NORMAL));
if (error)
g_printerr ("%s", error->message);
g_printerr ("\n");
g_printerr ("Try \"%s --help\" for more information.", g_get_prgname ());
g_printerr ("\n");
return EXIT_FAILURE;
}
int
usage_error_need_arg (const char *arg)
{
g_autoptr(GError) error = NULL;
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"missing argument '%s'", arg);
return usage_error (error);
}
int
usage_error_too_many_args (void)
{
g_autoptr(GError) error = NULL;
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"too many arguments");
return usage_error (error);
}
int
report_error (const char *prefix, GError *error)
{
g_printerr ("%s:", g_get_application_name ());
g_printerr ("%s error: %s", bolt_color (ANSI_RED), bolt_color (ANSI_NORMAL));
if (prefix)
g_printerr ("%s", prefix);
if (error)
{
if (g_dbus_error_is_remote_error (error))
g_dbus_error_strip_remote_error (error);
if (prefix)
g_printerr (": ");
g_printerr ("%s", error->message);
}
g_printerr ("\n");
return EXIT_FAILURE;
}
/* device related commands */
static gboolean
format_timestamp (BoltDevice *dev,
char *buffer,
gsize n,
const char *timesel)
{
g_autofree char *str = NULL;
guint64 ts;
g_object_get (dev, timesel, &ts, NULL);
if (ts > 0)
{
str = bolt_epoch_format (ts, "%c");
g_utf8_strncpy (buffer, str, n);
}
else
{
g_utf8_strncpy (buffer, "no", n);
}
return ts > 0;
}
static gboolean
format_generation (guint generation, char *buffer, size_t n)
{
switch (generation)
{
case 1:
case 2:
case 3:
g_snprintf (buffer, n, "Thunderbolt %u", generation);
return TRUE;
case 4:
g_snprintf (buffer, n, "USB4");
return TRUE;
case 0:
g_snprintf (buffer, n, "Unknown");
return FALSE;
default:
g_snprintf (buffer, n, "%u", generation);
return TRUE;
}
}
void
print_device (BoltDevice *dev, gboolean verbose)
{
g_autofree char *label = NULL;
const char *path;
const char *uid;
const char *name;
const char *vendor;
const char *syspath;
const char *parent;
BoltDeviceType type;
BoltStatus status;
BoltAuthFlags aflags;
BoltKeyState keystate;
BoltPolicy policy;
const char *status_color;
const char *status_symbol;
const char *type_text;
const char *status_text;
const char *tree_branch;
const char *tree_right;
const char *tree_space;
gboolean stored;
gboolean pcie;
char buf[256];
guint gen;
path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (dev));
uid = bolt_device_get_uid (dev);
name = bolt_device_get_name (dev);
vendor = bolt_device_get_vendor (dev);
gen = bolt_device_get_generation (dev);
type = bolt_device_get_device_type (dev);
status = bolt_device_get_status (dev);
aflags = bolt_device_get_authflags (dev);
parent = bolt_device_get_parent (dev);
syspath = bolt_device_get_syspath (dev);
stored = bolt_device_is_stored (dev);
policy = bolt_device_get_policy (dev);
keystate = bolt_device_get_keystate (dev);
pcie = bolt_flag_isclear (aflags, BOLT_AUTH_NOPCIE);
status_symbol = bolt_glyph (BLACK_CIRCLE);
tree_branch = bolt_glyph (TREE_BRANCH);
tree_right = bolt_glyph (TREE_RIGHT);
tree_space = bolt_glyph (TREE_SPACE);
switch (status)
{
case BOLT_STATUS_DISCONNECTED:
status_symbol = bolt_glyph (WHITE_CIRCLE);
status_color = bolt_color (ANSI_NORMAL);
status_text = "disconnected";
break;
case BOLT_STATUS_CONNECTING:
status_color = bolt_color (ANSI_YELLOW);
status_text = "connecting";
break;
case BOLT_STATUS_CONNECTED:
status_color = bolt_color (ANSI_YELLOW);
status_text = "connected";
break;
case BOLT_STATUS_AUTHORIZED:
case BOLT_STATUS_AUTHORIZED_NEWKEY:
case BOLT_STATUS_AUTHORIZED_SECURE:
case BOLT_STATUS_AUTHORIZED_DPONLY:
if (pcie)
{
status_color = bolt_color (ANSI_GREEN);
status_text = "authorized";
}
else
{
status_color = bolt_color (ANSI_BLUE);
status_text = "connected (no PCIe tunnels)";
}
break;
case BOLT_STATUS_AUTH_ERROR:
status_color = bolt_color (ANSI_RED);
status_text = "authorization error";
break;
default:
status_color = bolt_color (ANSI_NORMAL);
status_text = "unknown";
break;
}
label = bolt_device_get_display_name (dev);
g_print (" %s%s%s %s\n",
status_color,
status_symbol,
bolt_color (ANSI_NORMAL),
label ? : name);
type_text = bolt_device_type_to_string (type);
g_print (" %s type: %s\n", tree_branch, type_text);
g_print (" %s name: %s\n", tree_branch, name);
g_print (" %s vendor: %s\n", tree_branch, vendor);
g_print (" %s uuid: %s\n", tree_branch, format_uid (uid));
if (verbose)
g_print (" %s dbus path: %s\n", tree_branch, path);
if (format_generation (gen, buf, sizeof (buf)) || verbose)
g_print (" %s generation: %s\n", tree_branch, buf);
g_print (" %s status: %s\n", tree_branch, status_text);
if (bolt_status_is_connected (status))
{
g_autofree char *flags = NULL;
const char *domain;
BoltLinkSpeed speed = {0, };
domain = bolt_device_get_domain (dev);
g_print (" %s %s domain: %s\n",
bolt_glyph (TREE_VERTICAL),
tree_branch,
domain);
if (verbose)
{
g_print (" %s %s parent: %s\n",
bolt_glyph (TREE_VERTICAL),
tree_branch,
parent);
g_print (" %s %s syspath: %s\n",
bolt_glyph (TREE_VERTICAL),
tree_branch,
syspath);
}
bolt_device_get_linkspeed (dev, &speed);
if (speed.rx.lanes && speed.rx.speed)
{
g_print (" %s %s rx speed: %u Gb/s = %u lanes * %u Gb/s\n",
bolt_glyph (TREE_VERTICAL),
tree_branch,
speed.rx.lanes * speed.rx.speed,
speed.rx.lanes, speed.rx.speed);
}
if (speed.tx.lanes && speed.tx.speed)
{
g_print (" %s %s tx speed: %u Gb/s = %u lanes * %u Gb/s\n",
bolt_glyph (TREE_VERTICAL),
tree_branch,
speed.tx.lanes * speed.tx.speed,
speed.tx.lanes, speed.tx.speed);
}
flags = bolt_flags_to_string (BOLT_TYPE_AUTH_FLAGS, aflags, NULL);
g_print (" %s %s authflags: %s\n",
bolt_glyph (TREE_VERTICAL),
tree_right,
flags);
}
if (format_timestamp (dev, buf, sizeof (buf), "authtime"))
g_print (" %s authorized: %s\n", tree_branch, buf);
if (format_timestamp (dev, buf, sizeof (buf), "conntime"))
g_print (" %s connected: %s\n", tree_branch, buf);
format_timestamp (dev, buf, sizeof (buf), "storetime");
g_print (" %s stored: %s\n", tree_right, buf);
if (stored)
{
const char *pstr = bolt_policy_to_string (policy);
const char *kstr;
if (keystate == BOLT_KEY_MISSING)
kstr = "no";
else if (keystate == BOLT_KEY_HAVE)
kstr = "yes";
else if (keystate == BOLT_KEY_NEW)
kstr = "yes (new)";
else
kstr = "unknown";
g_print (" %s %s policy: %s\n", tree_space, tree_branch, pstr);
g_print (" %s %s key: %s\n", tree_space, tree_right, kstr);
}
g_print ("\n");
}
/* **** */
static SubCommand subcommands[] = {
{"authorize", authorize, "Authorize a device"},
{"config", config, "Get or set global, device or domain properties"},
{"domains", list_domains, "List the active thunderbolt domains"},
{"enroll", enroll, "Authorize and store a device in the database"},
{"forget", forget, "Remove a stored device from the database"},
{"info", info, "Show information about a device"},
{"list", list_devices, "List connected and stored devices"},
{"monitor", monitor, "Listen and print changes"},
{"power", power, "Force power configuration of the controller"},
{NULL, NULL, NULL},
};
char *
subcommands_make_summary (const SubCommand *cmds)
{
GString *s = g_string_new ("Commands:");
unsigned int spacing = 0;
for (const SubCommand *c = cmds; c && c->name; c++)
spacing = MAX (spacing, strlen (c->name));
spacing = MAX (15, spacing) + 2;
for (const SubCommand *c = cmds; c && c->name; c++)
{
int space = spacing - strlen (c->name);
g_string_append_printf (s, "\n %s", c->name);
g_string_append_printf (s, "%*s%s", space, "", c->desc);
}
return g_string_free (s, FALSE);
}
const SubCommand *
subcommands_find (const SubCommand *cmds,
const char *cmdname,
GError **error)
{
const SubCommand *cmd = NULL;
for (const SubCommand *c = cmds; !cmd && c && c->name; c++)
if (g_str_equal (cmdname, c->name))
cmd = c;
if (cmd == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"Invalid command: %s", cmdname);
return NULL;
}
return cmd;
}
int
subcommand_run (const SubCommand *cmd,
BoltClient *client,
int argc,
char **argv)
{
g_autofree char *cmdline = NULL;
g_autofree char **args = NULL;
int count;
cmdline = g_strconcat (g_get_prgname (),
" ",
cmd->name,
NULL);
g_set_prgname (cmdline);
count = MAX (argc - 1, 1);
args = g_new0 (char *, count + 1);
args[0] = cmdline;
for (int i = 1; i < count; i++)
args[i] = argv[i + 1];
return cmd->fn (client, count, args);
}
int
main (int argc, char **argv)
{
g_autoptr(GOptionContext) optctx = NULL;
g_autoptr(BoltClient) client = NULL;
g_autoptr(GError) error = NULL;
g_autofree char *summary = NULL;
const SubCommand *cmd = NULL;
const char *cmdname = NULL;
const char *uuid_fmtstr = "full";
gboolean version = FALSE;
int fmt = -1;
GOptionEntry options[] = {
{ "version", 0, 0, G_OPTION_ARG_NONE, &version, "Print version information and exit", NULL },
{ "uuids", 'U', 0, G_OPTION_ARG_STRING, &uuid_fmtstr, "How to format uuids [*full, short, alias]", NULL },
{ NULL }
};
setlocale (LC_ALL, "");
optctx = g_option_context_new ("[COMMAND]");
g_option_context_add_main_entries (optctx, options, NULL);
summary = subcommands_make_summary (subcommands);
g_option_context_set_summary (optctx, summary);
g_option_context_set_strict_posix (optctx, TRUE);
if (!g_option_context_parse (optctx, &argc, &argv, &error))
return usage_error (error);
if (version)
{
g_print ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
exit (EXIT_SUCCESS);
}
if (argc < 2)
cmdname = "list";
else
cmdname = argv[1];
fmt = format_uid_init (uuid_fmtstr, &error);
if (fmt == -1)
return usage_error (error);
client = bolt_client_new (&error);
if (!client)
return report_error ("could not create client", error);
cmd = subcommands_find (subcommands, cmdname, &error);
if (cmd == NULL)
return usage_error (error);
return subcommand_run (cmd, client, argc, argv);
}
bolt-0.9.2/cli/boltctl.h 0000664 0000000 0000000 00000004002 14174530510 0015041 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-client.h"
#include "bolt-term.h"
G_BEGIN_DECLS
typedef int (*run_t)(BoltClient *client,
int argc,
char **argv);
typedef struct SubCommand
{
const char *name;
run_t fn;
const char *desc;
} SubCommand;
char * subcommands_make_summary (const SubCommand *cmds);
const SubCommand * subcommands_find (const SubCommand *cmds,
const char *cmdname,
GError **error);
int subcommand_run (const SubCommand *cmd,
BoltClient *client,
int argc,
char **argv);
gboolean check_argc (int argc,
int lower,
int upper,
GError **error);
int usage_error (GError *error);
int usage_error_need_arg (const char *arg);
int usage_error_too_many_args (void);
int report_error (const char *prefix,
GError *error);
void print_device (BoltDevice *dev,
gboolean verbose);
G_END_DECLS
bolt-0.9.2/common/ 0000775 0000000 0000000 00000000000 14174530510 0013752 5 ustar 00root root 0000000 0000000 bolt-0.9.2/common/bolt-dbus.c 0000664 0000000 0000000 00000007754 14174530510 0016026 0 ustar 00root root 0000000 0000000 /*
* Copyright © 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-dbus.h"
#include "bolt-error.h"
#include "bolt-dbus-resource.h"
#include "bolt-str.h"
static gpointer
register_dbus_resources (gpointer data)
{
g_resources_register (bolt_dbus_get_resource ());
return NULL;
}
void
bolt_dbus_ensure_resources (void)
{
static GOnce only_once = G_ONCE_INIT;
g_once (&only_once, register_dbus_resources, NULL);
}
GDBusInterfaceInfo *
bolt_dbus_interface_info_find (const char *interface_xml,
const char *interface_name,
GError **error)
{
g_autoptr(GDBusNodeInfo) node = NULL;
GDBusInterfaceInfo **iter;
GDBusInterfaceInfo *info = NULL;
g_return_val_if_fail (interface_xml != NULL, NULL);
g_return_val_if_fail (interface_name != NULL, NULL);
node = g_dbus_node_info_new_for_xml (interface_xml, error);
if (node == NULL)
return NULL;
for (iter = node->interfaces; iter && *iter; iter++)
{
GDBusInterfaceInfo *ii = *iter;
if (bolt_streq (ii->name, interface_name))
{
info = ii;
break;
}
}
if (info == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"could not find interface with name '%s",
interface_name);
return NULL;
}
return g_dbus_interface_info_ref (info);
}
GDBusInterfaceInfo *
bolt_dbus_interface_info_lookup (const char *resource_name,
const char *interface_name,
GError **error)
{
g_autoptr(GBytes) data = NULL;
GDBusInterfaceInfo *info;
const char *xml;
g_return_val_if_fail (resource_name != NULL, NULL);
g_return_val_if_fail (interface_name != NULL, NULL);
data = g_resources_lookup_data (resource_name,
G_RESOURCE_LOOKUP_FLAGS_NONE,
error);
if (data == NULL)
return NULL;
xml = g_bytes_get_data (data, NULL);
info = bolt_dbus_interface_info_find (xml, interface_name, error);
return info;
}
gboolean
bolt_dbus_get_sender_pid (GDBusMethodInvocation *invocation,
guint *pid,
GError **error)
{
g_autoptr(GVariant) res = NULL;
g_autoptr(GError) err = NULL;
GDBusConnection *con;
const char *sender;
con = g_dbus_method_invocation_get_connection (invocation);
sender = g_dbus_method_invocation_get_sender (invocation);
res = g_dbus_connection_call_sync (con,
"org.freedesktop.DBus",
"/",
"org.freedesktop.DBus",
"GetConnectionUnixProcessID",
g_variant_new ("(s)", sender),
G_VARIANT_TYPE ("(u)"),
G_DBUS_CALL_FLAGS_NONE,
-1, NULL,
&err);
if (res == NULL)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"could not get pid of caller: %s",
err->message);
return FALSE;
}
g_variant_get (res, "(u)", pid);
return TRUE;
}
bolt-0.9.2/common/bolt-dbus.h 0000664 0000000 0000000 00000003131 14174530510 0016014 0 ustar 00root root 0000000 0000000 /*
* Copyright © 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
void bolt_dbus_ensure_resources (void);
GDBusInterfaceInfo * bolt_dbus_interface_info_find (const char *interface_xml,
const char *interface_name,
GError **error);
GDBusInterfaceInfo * bolt_dbus_interface_info_lookup (const char *resource_name,
const char *interface_name,
GError **error);
gboolean bolt_dbus_get_sender_pid (GDBusMethodInvocation *invocation,
guint *pid,
GError **error);
G_END_DECLS
bolt-0.9.2/common/bolt-enums.c 0000664 0000000 0000000 00000026032 14174530510 0016206 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-enums.h"
#include "bolt-error.h"
#include
#if !GLIB_CHECK_VERSION (2, 57, 0)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GEnumClass, g_type_class_unref);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GFlagsClass, g_type_class_unref);
#endif
gboolean
bolt_enum_class_validate (GEnumClass *enum_class,
gint value,
GError **error)
{
const char *name;
gboolean oob;
g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
oob = value < enum_class->minimum || value > enum_class->maximum;
if (oob)
{
name = g_type_name_from_class ((GTypeClass *) enum_class);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"enum value '%d' is out of bounds for '%s'",
value, name);
return FALSE;
}
return TRUE;
}
gboolean
bolt_enum_validate (GType enum_type,
gint value,
GError **error)
{
g_autoptr(GEnumClass) klass = NULL;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
klass = g_type_class_ref (enum_type);
return bolt_enum_class_validate (klass, value, error);
}
const char *
bolt_enum_class_to_string (GEnumClass *klass,
gint value,
GError **error)
{
GEnumValue *ev;
g_return_val_if_fail (G_IS_ENUM_CLASS (klass), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (!bolt_enum_class_validate (klass, value, error))
return NULL;
ev = g_enum_get_value (klass, value);
return ev->value_nick;
}
gboolean
bolt_enum_class_from_string (GEnumClass *klass,
const char *string,
gint *enum_out,
GError **error)
{
const char *name;
GEnumValue *ev;
g_return_val_if_fail (G_IS_ENUM_CLASS (klass), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (string == NULL)
{
name = g_type_name_from_class ((GTypeClass *) klass);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"empty string passed for enum class for '%s'",
name);
return FALSE;
}
ev = g_enum_get_value_by_nick (klass, string);
if (ev == NULL)
{
name = g_type_name_from_class ((GTypeClass *) klass);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"invalid string '%s' for enum '%s'", string, name);
return FALSE;
}
if (enum_out)
*enum_out = ev->value;
return TRUE;
}
const char *
bolt_enum_to_string (GType enum_type,
gint value,
GError **error)
{
g_autoptr(GEnumClass) klass = NULL;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
klass = g_type_class_ref (enum_type);
return bolt_enum_class_to_string (klass, value, error);
}
gint
bolt_enum_from_string (GType enum_type,
const char *string,
GError **error)
{
g_autoptr(GEnumClass) klass = NULL;
gint iv = -1;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
klass = g_type_class_ref (enum_type);
bolt_enum_class_from_string (klass, string, &iv, error);
return iv;
}
char *
bolt_flags_class_to_string (GFlagsClass *flags_class,
guint value,
GError **error)
{
g_autoptr(GString) str = NULL;
const char *name;
GFlagsValue *fv;
g_return_val_if_fail (G_IS_FLAGS_CLASS (flags_class), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
fv = g_flags_get_first_value (flags_class, value);
if (fv == NULL)
{
if (value == 0)
return g_strdup ("");
name = g_type_name_from_class ((GTypeClass *) flags_class);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"invalid value '%u' for flags '%s'", value, name);
return NULL;
}
value &= ~fv->value;
str = g_string_new (fv->value_nick);
while (value != 0 &&
(fv = g_flags_get_first_value (flags_class, value)) != NULL)
{
g_string_append (str, " | ");
g_string_append (str, fv->value_nick);
value &= ~fv->value;
}
if (value != 0)
{
name = g_type_name_from_class ((GTypeClass *) flags_class);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"unhandled value '%u' for flags '%s'", value, name);
return NULL;
}
return g_string_free (g_steal_pointer (&str), FALSE);
}
gboolean
bolt_flags_class_from_string (GFlagsClass *flags_class,
const char *string,
guint *flags_out,
GError **error)
{
g_auto(GStrv) vals = NULL;
const char *name;
guint flags = 0;
g_return_val_if_fail (G_IS_FLAGS_CLASS (flags_class), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (string == NULL)
{
name = g_type_name_from_class ((GTypeClass *) flags_class);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"empty string passed for flags class for '%s'",
name);
return FALSE;
}
vals = g_strsplit (string, "|", -1);
for (guint i = 0; vals[i]; i++)
{
GFlagsValue *fv;
char *nick;
nick = g_strstrip (vals[i]);
fv = g_flags_get_value_by_nick (flags_class, nick);
if (fv == NULL)
{
name = g_type_name_from_class ((GTypeClass *) flags_class);
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"invalid flag '%s' for flags '%s'", string, name);
return FALSE;
}
flags |= fv->value;
}
if (flags_out != NULL)
*flags_out = flags;
return TRUE;
}
char *
bolt_flags_to_string (GType flags_type,
guint value,
GError **error)
{
g_autoptr(GFlagsClass) klass = NULL;
g_return_val_if_fail (G_TYPE_IS_FLAGS (flags_type), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
klass = g_type_class_ref (flags_type);
return bolt_flags_class_to_string (klass, value, error);
}
gboolean
bolt_flags_from_string (GType flags_type,
const char *string,
guint *flags_out,
GError **error)
{
g_autoptr(GFlagsClass) klass = NULL;
g_return_val_if_fail (G_TYPE_IS_FLAGS (flags_type), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
klass = g_type_class_ref (flags_type);
return bolt_flags_class_from_string (klass, string, flags_out, error);
}
gboolean
bolt_flags_update (guint from,
guint *to,
guint mask)
{
guint val;
gboolean chg;
g_return_val_if_fail (to != NULL, FALSE);
val = *to & ~mask; /* clear all bits in mask */
val = val | (from & mask); /* set all bits in from and mask */
chg = *to != val;
*to = val;
return chg;
}
const char *
bolt_status_to_string (BoltStatus status)
{
return bolt_enum_to_string (BOLT_TYPE_STATUS, status, NULL);
}
gboolean
bolt_status_is_authorized (BoltStatus status)
{
return status == BOLT_STATUS_AUTHORIZED ||
status == BOLT_STATUS_AUTHORIZED_SECURE ||
status == BOLT_STATUS_AUTHORIZED_NEWKEY;
}
gboolean
bolt_status_is_pending (BoltStatus status)
{
return status == BOLT_STATUS_AUTH_ERROR ||
status == BOLT_STATUS_CONNECTED;
}
gboolean
bolt_status_is_connected (BoltStatus status)
{
return status > BOLT_STATUS_DISCONNECTED;
}
BoltSecurity
bolt_security_from_string (const char *str)
{
return bolt_enum_from_string (BOLT_TYPE_SECURITY, str, NULL);
}
const char *
bolt_security_to_string (BoltSecurity security)
{
return bolt_enum_to_string (BOLT_TYPE_SECURITY, security, NULL);
}
gboolean
bolt_security_allows_pcie (BoltSecurity security)
{
gboolean pcie = FALSE;
switch (security)
{
case BOLT_SECURITY_NONE:
case BOLT_SECURITY_USER:
case BOLT_SECURITY_SECURE:
pcie = TRUE;
break;
case BOLT_SECURITY_DPONLY:
case BOLT_SECURITY_USBONLY:
case BOLT_SECURITY_UNKNOWN:
pcie = FALSE;
break;
}
return pcie;
}
gboolean
bolt_security_is_interactive (BoltSecurity security)
{
gboolean interactive = FALSE;
switch (security)
{
case BOLT_SECURITY_USER:
case BOLT_SECURITY_SECURE:
interactive = TRUE;
break;
case BOLT_SECURITY_NONE:
case BOLT_SECURITY_DPONLY:
case BOLT_SECURITY_USBONLY:
case BOLT_SECURITY_UNKNOWN:
interactive = FALSE;
break;
}
return interactive;
}
const char *
bolt_security_for_display (BoltSecurity security,
gboolean iommu)
{
switch (security)
{
case BOLT_SECURITY_NONE:
return iommu ? "iommu" : "none";
case BOLT_SECURITY_USER:
return iommu ? "iommu+user" : "user";
case BOLT_SECURITY_SECURE:
return iommu ? "iommu+secure" : "secure";
break;
default:
return bolt_security_to_string (security);
}
}
BoltPolicy
bolt_policy_from_string (const char *str)
{
return bolt_enum_from_string (BOLT_TYPE_POLICY, str, NULL);
}
const char *
bolt_policy_to_string (BoltPolicy policy)
{
return bolt_enum_to_string (BOLT_TYPE_POLICY, policy, NULL);
}
gboolean
bolt_policy_validate (BoltPolicy policy)
{
return bolt_enum_validate (BOLT_TYPE_POLICY, policy, NULL);
}
BoltDeviceType
bolt_device_type_from_string (const char *str)
{
return bolt_enum_from_string (BOLT_TYPE_DEVICE_TYPE, str, NULL);
}
const char *
bolt_device_type_to_string (BoltDeviceType type)
{
return bolt_enum_to_string (BOLT_TYPE_DEVICE_TYPE, type, NULL);
}
gboolean
bolt_device_type_is_host (BoltDeviceType type)
{
return type == BOLT_DEVICE_HOST;
}
char *
bolt_auth_mode_to_string (BoltAuthMode mode)
{
return bolt_flags_to_string (BOLT_TYPE_AUTH_MODE, mode, NULL);
}
const char *
bolt_power_state_to_string (BoltPowerState state)
{
return bolt_enum_to_string (BOLT_TYPE_POWER_STATE, state, NULL);
}
bolt-0.9.2/common/bolt-enums.h 0000664 0000000 0000000 00000022405 14174530510 0016213 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-names.h"
#include "bolt-enum-types.h"
gboolean bolt_enum_validate (GType enum_type,
gint value,
GError **error);
gboolean bolt_enum_class_validate (GEnumClass *enum_class,
gint value,
GError **error);
const char * bolt_enum_class_to_string (GEnumClass *enum_class,
gint value,
GError **error);
gboolean bolt_enum_class_from_string (GEnumClass *enum_class,
const char *string,
gint *enum_out,
GError **error);
const char * bolt_enum_to_string (GType enum_type,
gint value,
GError **error);
gint bolt_enum_from_string (GType enum_type,
const char *string,
GError **error);
char * bolt_flags_class_to_string (GFlagsClass *flags_class,
guint value,
GError **error);
gboolean bolt_flags_class_from_string (GFlagsClass *flags_class,
const char *string,
guint *flags_out,
GError **error);
char * bolt_flags_to_string (GType flags_type,
guint value,
GError **error);
gboolean bolt_flags_from_string (GType flags_type,
const char *string,
guint *flags_out,
GError **error);
gboolean bolt_flags_update (guint from,
guint *to,
guint mask);
#define bolt_flag_isset(flags_, flag_) (!!(flags_ & flag_))
#define bolt_flag_isclear(flags_, flag_) (!(flags_ & flag_))
/**
* BoltStatus:
* @BOLT_STATUS_UNKNOWN: Device is in an unknown state (should normally not happen).
* @BOLT_STATUS_DISCONNECTED: Device is not connected.
* @BOLT_STATUS_CONNECTING: Device is currently being connected.
* @BOLT_STATUS_CONNECTED: Device is connected, but not authorized.
* @BOLT_STATUS_AUTHORIZING: Device is currently authorizing.
* @BOLT_STATUS_AUTH_ERROR: Failed to authorize a device via a key.
* @BOLT_STATUS_AUTHORIZED: Device connected and authorized.
* @BOLT_STATUS_AUTHORIZED_SECURE: Device connected and securely authorized via a key (deprecated).
* @BOLT_STATUS_AUTHORIZED_NEWKEY: Device connected and authorized via a new key (deprecated).
* @BOLT_STATUS_AUTHORIZED_DPONLY: Device authorized but with thunderbolt disabled (deprecated).
*
* The current status of the device.
*/
typedef enum {
BOLT_STATUS_UNKNOWN = -1,
BOLT_STATUS_DISCONNECTED = 0,
BOLT_STATUS_CONNECTING,
BOLT_STATUS_CONNECTED,
BOLT_STATUS_AUTHORIZING,
BOLT_STATUS_AUTH_ERROR,
BOLT_STATUS_AUTHORIZED,
/* deprecated, do not use */
BOLT_STATUS_AUTHORIZED_SECURE,
BOLT_STATUS_AUTHORIZED_NEWKEY,
BOLT_STATUS_AUTHORIZED_DPONLY
} BoltStatus;
const char * bolt_status_to_string (BoltStatus status);
gboolean bolt_status_is_authorized (BoltStatus status);
gboolean bolt_status_is_connected (BoltStatus status);
gboolean bolt_status_is_pending (BoltStatus status);
/**
* BoltAuthFlags:
* @BOLT_AUTH_NONE: No specific authorization.
* @BOLT_AUTH_NOPCIE: PCIe tunnels are *not* authorized.
* @BOLT_AUTH_SECURE: Device is securely authorized.
* @BOLT_AUTH_NOKEY: Device does *not* support key verification.
* @BOLT_AUTH_BOOT: Device was already authorized during pre-boot.
*
* More specific information about device authorization.
*/
typedef enum { /*< flags >*/
BOLT_AUTH_NONE = 0,
BOLT_AUTH_NOPCIE = 1 << 0,
BOLT_AUTH_SECURE = 1 << 1,
BOLT_AUTH_NOKEY = 1 << 2,
BOLT_AUTH_BOOT = 1 << 3,
} BoltAuthFlags;
/**
* BoltKeyState:
* @BOLT_KEY_UNKNOWN: unknown key state
* @BOLT_KEY_MISSING: no key
* @BOLT_KEY_HAVE: key exists
* @BOLT_KEY_NEW: key is new
*
* The state of the key.
*/
typedef enum {
BOLT_KEY_UNKNOWN = -1,
BOLT_KEY_MISSING = 0,
BOLT_KEY_HAVE = 1,
BOLT_KEY_NEW = 2
} BoltKeyState;
/**
* BoltSecurity:
* @BOLT_SECURITY_UNKNOWN : Unknown security.
* @BOLT_SECURITY_NONE : No security, all devices are automatically connected.
* @BOLT_SECURITY_DPONLY : Display Port only devices only.
* @BOLT_SECURITY_USER : User needs to authorize devices.
* @BOLT_SECURITY_SECURE : User needs to authorize devices. Authorization can
* be done via key exchange to verify the device identity.
* @BOLT_SECURITY_USBONLY : Only create a PCIe tunnel to the USB controller in a
* connected thunderbolt dock, allowing no downstream PCIe tunnels.
*
* The security level of the thunderbolt domain.
*/
typedef enum {
BOLT_SECURITY_UNKNOWN = -1,
BOLT_SECURITY_NONE = 0,
BOLT_SECURITY_DPONLY = 1,
BOLT_SECURITY_USER = '1',
BOLT_SECURITY_SECURE = '2',
BOLT_SECURITY_USBONLY = 4,
} BoltSecurity;
BoltSecurity bolt_security_from_string (const char *str);
const char * bolt_security_to_string (BoltSecurity security);
gboolean bolt_security_allows_pcie (BoltSecurity security);
gboolean bolt_security_is_interactive (BoltSecurity security);
const char * bolt_security_for_display (BoltSecurity security,
gboolean iommu);
/**
* BoltPolicy:
* @BOLT_POLICY_UNKNOWN: Unknown policy.
* @BOLT_POLICY_DEFAULT: Default policy.
* @BOLT_POLICY_MANUAL: Manual authorization of the device.
* @BOLT_POLICY_AUTO: Connect the device automatically,
* with the best possible security level supported
* by the domain controller.
* @BOLT_POLICY_IOMMU: Like @BOLT_POLICY_AUTO if IOMMU, i.e.
* hardware DMA protection, is active and like @BOLT_POLICY_MANUAL
* otherwise: automatically authorize the device if and only if
* we have IOMMU active support.
*
* What do to for connected devices.
*/
typedef enum {
BOLT_POLICY_UNKNOWN = -1,
BOLT_POLICY_DEFAULT = 0,
BOLT_POLICY_MANUAL = 1,
BOLT_POLICY_AUTO = 2,
BOLT_POLICY_IOMMU = 3,
} BoltPolicy;
BoltPolicy bolt_policy_from_string (const char *str);
const char * bolt_policy_to_string (BoltPolicy policy);
gboolean bolt_policy_validate (BoltPolicy policy);
/**
* BoltAuthCtrl:
* @BOLT_AUTHCTRL_NONE: No authorization flags.
*
* Control authorization.
*/
typedef enum { /*< flags >*/
BOLT_AUTHCTRL_NONE = 0
} BoltAuthCtrl;
/**
* BoltDeviceType:
* @BOLT_DEVICE_UNKNOWN_TYPE: Unknown device type
* @BOLT_DEVICE_HOST: The device representing the host
* @BOLT_DEVICE_PERIPHERAL: A generic thunderbolt peripheral
*
* The type of the device.
*/
typedef enum {
BOLT_DEVICE_UNKNOWN_TYPE = -1,
BOLT_DEVICE_HOST = 0,
BOLT_DEVICE_PERIPHERAL
} BoltDeviceType;
BoltDeviceType bolt_device_type_from_string (const char *str);
const char * bolt_device_type_to_string (BoltDeviceType type);
gboolean bolt_device_type_is_host (BoltDeviceType type);
/**
* BoltAuthMode:
* @BOLT_AUTH_DISABLED: Authorization is disabled
* @BOLT_AUTH_ENABLED: Authorization is enabled.
*
* Control authorization.
*/
typedef enum { /*< flags >*/
BOLT_AUTH_DISABLED = 0,
BOLT_AUTH_ENABLED = 1
} BoltAuthMode;
#define bolt_auth_mode_is_enabled(auth) ((auth & BOLT_AUTH_ENABLED) != 0)
#define bolt_auth_mode_is_disabled(auth) (!bolt_auth_mode_is_enabled (auth))
char * bolt_auth_mode_to_string (BoltAuthMode mode);
/**
* BoltPowerState:
* @BOLT_FORCE_POWER_UNSET: Force power was not set by bolt.
* @BOLT_FORCE_POWER_OFF: Force power is set to off.
* @BOLT_FORCE_POWER_ON: Force power is set to on.
* @BOLT_FORCE_POWER_WAIT: Force power is not requested anymore
* but still active; so that code can process udev events
* properly.
*
* The force power state that bolt set on thunderbolt controller.
*/
typedef enum {
BOLT_FORCE_POWER_UNSET = -1,
BOLT_FORCE_POWER_OFF = 0,
BOLT_FORCE_POWER_ON = 1,
BOLT_FORCE_POWER_WAIT = 2,
} BoltPowerState;
const char * bolt_power_state_to_string (BoltPowerState state);
bolt-0.9.2/common/bolt-error.c 0000664 0000000 0000000 00000007646 14174530510 0016222 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-error.h"
#include "bolt-names.h"
#include
/**
* SECTION:bolt-error
* @Title: Error codes
*
*/
static const GDBusErrorEntry bolt_error_entries[] = {
{BOLT_ERROR_FAILED, BOLT_DBUS_NAME ".Error.Failed"},
{BOLT_ERROR_UDEV, BOLT_DBUS_NAME ".Error.UDev"},
{BOLT_ERROR_NOKEY, BOLT_DBUS_NAME ".Error.NoKey"},
{BOLT_ERROR_BADKEY, BOLT_DBUS_NAME ".Error.BadKey"},
{BOLT_ERROR_CFG, BOLT_DBUS_NAME ".Error.Cfg"},
{BOLT_ERROR_BADSTATE, BOLT_DBUS_NAME ".Error.BadState"},
{BOLT_ERROR_AUTHCHAIN, BOLT_DBUS_NAME ".Error.AuthChain"},
};
GQuark
bolt_error_quark (void)
{
static volatile gsize quark_volatile = 0;
g_dbus_error_register_error_domain ("bolt-error-quark",
&quark_volatile,
bolt_error_entries,
G_N_ELEMENTS (bolt_error_entries));
return (GQuark) quark_volatile;
}
gboolean
bolt_err_notfound (const GError *error)
{
return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) ||
g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) ||
g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND);
}
gboolean
bolt_err_exists (const GError *error)
{
return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS) ||
g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_EXIST);
}
gboolean
bolt_err_inval (const GError *error)
{
return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
}
gboolean
bolt_err_cancelled (const GError *error)
{
return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
}
gboolean
bolt_err_badstate (const GError *error)
{
return g_error_matches (error, BOLT_ERROR, BOLT_ERROR_BADSTATE);
}
gboolean
bolt_err_nokey (const GError *error)
{
return g_error_matches (error, BOLT_ERROR, BOLT_ERROR_NOKEY);
}
gboolean
bolt_error_propagate (GError **dest,
GError **source)
{
GError *src;
g_return_val_if_fail (source != NULL, FALSE);
src = *source;
if (src == NULL)
return TRUE;
g_propagate_error (dest, src);
*source = NULL;
return FALSE;
}
gboolean
bolt_error_propagate_stripped (GError **dest,
GError **source)
{
GError *src;
g_return_val_if_fail (source != NULL, FALSE);
src = *source;
if (src == NULL)
return TRUE;
if (g_dbus_error_is_remote_error (src))
g_dbus_error_strip_remote_error (src);
g_propagate_error (dest, g_steal_pointer (source));
return FALSE;
}
gboolean
bolt_error_for_errno (GError **error,
gint err_no,
const char *format,
...)
{
va_list ap;
int code;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_return_val_if_fail (format != NULL, FALSE);
if (err_no == 0)
return TRUE;
if (error == NULL)
return FALSE;
code = g_io_error_from_errno (err_no);
va_start (ap, format);
*error = g_error_new_valist (G_IO_ERROR, code, format, ap);
va_end (ap);
return FALSE;
}
bolt-0.9.2/common/bolt-error.h 0000664 0000000 0000000 00000004311 14174530510 0016211 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
/**
* BoltError:
* @BOLT_ERROR_FAILED: Generic error code
* @BOLT_ERROR_UDEV: UDev error
* @BOLT_ERROR_NOKEY: Key for authorization is missing
* @BOLT_ERROR_BADKEY: The key is invalid
* @BOLT_ERROR_CFG: Configuration is invalid
* @BOLT_ERROR_BADSTATE: Device is in the wrong state
* @BOLT_ERROR_AUTHCHAIN: Interrupted authorization chain
*
* Error codes used inside Bolt.
*/
typedef enum {
BOLT_ERROR_FAILED = 0,
BOLT_ERROR_UDEV,
BOLT_ERROR_NOKEY,
BOLT_ERROR_BADKEY,
BOLT_ERROR_CFG,
BOLT_ERROR_BADSTATE,
BOLT_ERROR_AUTHCHAIN,
} BoltError;
GQuark bolt_error_quark (void);
#define BOLT_ERROR (bolt_error_quark ())
/* helper function to check for certain error types */
gboolean bolt_err_notfound (const GError *error);
gboolean bolt_err_exists (const GError *error);
gboolean bolt_err_inval (const GError *error);
gboolean bolt_err_cancelled (const GError *error);
gboolean bolt_err_badstate (const GError *error);
gboolean bolt_err_nokey (const GError *error);
gboolean bolt_error_propagate (GError **dest,
GError **source);
gboolean bolt_error_propagate_stripped (GError **dest,
GError **source);
gboolean bolt_error_for_errno (GError **error,
gint err_no,
const char *format,
...) G_GNUC_PRINTF (3, 4);
G_END_DECLS
bolt-0.9.2/common/bolt-fs.c 0000664 0000000 0000000 00000007163 14174530510 0015473 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-error.h"
#include "bolt-io.h"
#include "bolt-fs.h"
#include
#include
gboolean
bolt_fs_make_parent_dirs (GFile *target,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autoptr(GFile) p = NULL;
gboolean ok;
g_return_val_if_fail (G_IS_FILE (target), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
p = g_file_get_parent (target);
ok = g_file_make_directory_with_parents (p, NULL, &err);
if (!ok && !bolt_err_exists (err))
return bolt_error_propagate (error, &err);
return TRUE;
}
static void
cleanup_dir (DIR *d)
{
struct dirent *de = NULL;
while ((de = readdir (d)) != NULL)
{
g_autoptr(GError) error = NULL;
int uflag = 0;
if (!g_strcmp0 (de->d_name, ".") ||
!g_strcmp0 (de->d_name, ".."))
continue;
if (de->d_type == DT_DIR)
{
DIR *cd;
cd = bolt_opendir_at (dirfd (d), de->d_name, O_RDONLY, &error);
if (cd == NULL)
continue;
cleanup_dir (cd);
uflag = AT_REMOVEDIR;
closedir (cd);
}
bolt_unlink_at (dirfd (d), de->d_name, uflag, NULL);
}
}
gboolean
bolt_fs_cleanup_dir (const char *target,
GError **error)
{
DIR *d = NULL;
g_return_val_if_fail (target != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
d = bolt_opendir (target, error);
if (d == NULL)
return FALSE;
cleanup_dir (d);
(void) bolt_closedir (d, NULL);
return bolt_rmdir (target, error);
}
#define TOUCH_FLAGS O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC
static inline void
timespec_set_from_uint64 (struct timespec *to,
guint64 from)
{
if (from > 0)
{
to->tv_sec = (time_t) from;
to->tv_nsec = 0;
}
else
{
to->tv_sec = 0;
to->tv_nsec = UTIME_OMIT;
}
}
gboolean
bolt_fs_touch (GFile *target,
guint64 atime,
guint64 mtime,
GError **error)
{
g_autofree char *path = NULL;
struct timespec times[2];
gboolean ok;
int fd;
int r;
g_return_val_if_fail (target != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
path = g_file_get_path (target);
fd = bolt_open (path, TOUCH_FLAGS, 0664, error);
if (fd < 0)
return FALSE;
/* times[0] is the "last access time" (atime) */
timespec_set_from_uint64 (×[0], atime);
/* times[1] is the "last modification time" (mtime). */
timespec_set_from_uint64 (×[1], mtime);
r = futimens (fd, times);
if (r == -1)
{
int errsave = errno;
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsave),
"could not touch file: %s", g_strerror (errsave));
}
ok = bolt_close (fd, error);
return r != -1 && ok;
}
bolt-0.9.2/common/bolt-fs.h 0000664 0000000 0000000 00000002366 14174530510 0015500 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include "bolt-error.h"
#include
G_BEGIN_DECLS
gboolean bolt_fs_make_parent_dirs (GFile *target,
GError **error);
gboolean bolt_fs_cleanup_dir (const char *target,
GError **error);
gboolean bolt_fs_touch (GFile *target,
guint64 atime,
guint64 mtime,
GError **error);
G_END_DECLS
bolt-0.9.2/common/bolt-glue.c 0000664 0000000 0000000 00000037124 14174530510 0016017 0 ustar 00root root 0000000 0000000 /*
* Copyright © 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-glue.h"
#include "bolt-enums.h"
#include "bolt-str.h"
#include
GParamSpec *
bolt_param_spec_override (GObjectClass *klass,
const char *name)
{
GObjectClass *parent_class = NULL;
GParamSpec *base = NULL;
GType parent;
GType type;
guint n;
g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL);
g_return_val_if_fail (name != NULL, NULL);
type = G_OBJECT_CLASS_TYPE (klass);
parent = g_type_parent (type);
parent_class = g_type_class_peek (parent);
if (parent_class)
base = g_object_class_find_property (parent_class, name);
if (base == NULL)
{
g_autofree GType *ifaces = g_type_interfaces (type, &n);
while (n-- && base == NULL)
{
gpointer iface = g_type_default_interface_peek (ifaces[n]);
if (iface)
base = g_object_interface_find_property (iface, name);
}
}
if (base == NULL)
{
g_critical ("Could not override unknown property: '%s::%s'",
G_OBJECT_CLASS_NAME (klass), name);
return NULL;
}
return g_param_spec_override (name, base);
}
gboolean
bolt_str_parse_by_pspec (GParamSpec *spec,
const char *str,
GValue *val,
GError **error)
{
gboolean ok;
if (val->g_type == 0)
g_value_init (val, spec->value_type);
g_return_val_if_fail (val->g_type == spec->value_type, FALSE);
if (G_IS_PARAM_SPEC_BOOLEAN (spec))
{
gboolean v;
ok = bolt_str_parse_as_boolean (str, &v, error);
if (!ok)
return FALSE;
g_value_set_boolean (val, (gboolean) v);
}
else if (G_IS_PARAM_SPEC_UINT (spec))
{
GParamSpecUInt *s = G_PARAM_SPEC_UINT (spec);
guint64 v;
ok = bolt_str_parse_as_uint64 (str, &v, error);
if (!ok)
return FALSE;
if (v < s->minimum || v > s->maximum)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"'%" G_GUINT64_FORMAT "' out of range for property",
v);
return FALSE;
}
g_value_set_uint (val, (guint) v);
}
else if (G_IS_PARAM_SPEC_UINT64 (spec))
{
GParamSpecUInt64 *s = G_PARAM_SPEC_UINT64 (spec);
guint64 v;
ok = bolt_str_parse_as_uint64 (str, &v, error);
if (!ok)
return FALSE;
if (v < s->minimum || v > s->maximum)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"'%" G_GUINT64_FORMAT "' out of range for property",
v);
return FALSE;
}
g_value_set_uint64 (val, (guint64) v);
}
else if (G_IS_PARAM_SPEC_ENUM (spec))
{
GParamSpecEnum *s = G_PARAM_SPEC_ENUM (spec);
gint v;
ok = bolt_enum_class_from_string (s->enum_class, str, &v, error);
if (!ok)
return FALSE;
g_value_set_enum (val, v);
}
else if (G_IS_PARAM_SPEC_FLAGS (spec))
{
GParamSpecFlags *s = G_PARAM_SPEC_FLAGS (spec);
guint v;
ok = bolt_flags_class_from_string (s->flags_class, str, &v, error);
if (!ok)
return FALSE;
g_value_set_flags (val, v);
}
else if (G_IS_PARAM_SPEC_STRING (spec))
{
g_value_set_string (val, str);
}
else if (G_IS_PARAM_SPEC_BOXED (spec) && spec->value_type == G_TYPE_STRV)
{
g_auto(GStrv) strv = NULL;
strv = g_strsplit (str, ",", -1);
g_value_take_boxed (val, g_steal_pointer (&strv));
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"parsing of '%s' is not supported",
g_type_name (spec->value_type));
return FALSE;
}
return TRUE;
}
GPtrArray *
bolt_properties_for_type (GType target)
{
g_autofree GParamSpec **specs = NULL;
GObjectClass *klass;
GPtrArray *props;
guint n;
klass = g_type_class_ref (target);
specs = g_object_class_list_properties (klass, &n);
props = g_ptr_array_new_full (n, (GDestroyNotify) g_param_spec_unref);
for (guint i = 0; i < n; i++)
{
GParamSpec *s = specs[i];
if (s->owner_type != target)
continue;
g_ptr_array_add (props, g_param_spec_ref (s));
}
return props;
}
gboolean
bolt_properties_find (GPtrArray *specs,
const char *name,
GParamSpec **spec,
GError **error)
{
if (name == NULL || specs == NULL)
return FALSE;
for (guint i = 0; i < specs->len; i++)
{
GParamSpec *s = g_ptr_array_index (specs, i);
if (g_str_equal (name, s->name) ||
bolt_streq (name, g_param_spec_get_nick (s)))
{
*spec = s;
return TRUE;
}
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"property '%s' not found", name);
return FALSE;
}
typedef enum {
BOLT_WIRE_CONV_NATIVE,
BOLT_WIRE_CONV_CUSTOM,
BOLT_WIRE_CONV_ENUM_AS_STRING,
BOLT_WIRE_CONV_FLAGS_AS_STRING,
BOLT_WIRE_CONV_OBJECT_AS_STRING,
} BoltWireConvType;
struct _BoltWireConv
{
/* book-keeping */
volatile gint ref_count;
/* */
GVariantType *wire_type;
GParamSpec *prop_spec;
/* */
BoltWireConvType conv_type;
BoltConvToWire to_wire;
BoltConvFromWire from_wire;
/* */
char *custom_id;
};
/* helper */
static inline gboolean
value_is_initialized (GValue *value)
{
return G_VALUE_TYPE (value) != 0;
}
static void
wire_conv_init_value_if_needed (BoltWireConv *conv,
GValue *value)
{
if (!value_is_initialized (value))
g_value_init (value, conv->prop_spec->value_type);
}
/* internal conversions */
static GVariant *
conv_enum_to_str (BoltWireConv *conv,
const GValue *value,
GError **error)
{
GParamSpecEnum *es = G_PARAM_SPEC_ENUM (conv->prop_spec);
const char *str = NULL;
gint iv;
iv = g_value_get_enum (value);
str = bolt_enum_class_to_string (es->enum_class, iv, error);
if (str == NULL)
return NULL;
return g_variant_new_string (str);
}
static gboolean
conv_enum_from_str (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error)
{
GParamSpecEnum *es;
const char *str;
gboolean ok;
gint v;
es = G_PARAM_SPEC_ENUM (conv->prop_spec);
str = g_variant_get_string (wire, NULL);
/* NB: it is ok to pass NULL for 'str' */
ok = bolt_enum_class_from_string (es->enum_class, str, &v, error);
if (ok)
g_value_set_enum (value, v);
else
g_value_set_enum (value, es->default_value);
return ok;
}
static GVariant *
conv_flags_to_str (BoltWireConv *conv,
const GValue *value,
GError **error)
{
GParamSpecFlags *fs;
char *str;
guint uv;
fs = G_PARAM_SPEC_FLAGS (conv->prop_spec);
uv = g_value_get_flags (value);
str = bolt_flags_class_to_string (fs->flags_class, uv, error);
if (str == NULL)
return NULL;
return g_variant_new_take_string (str);
}
static gboolean
conv_flags_from_str (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error)
{
GParamSpecFlags *fs;
gboolean ok;
const char *str;
guint v;
fs = G_PARAM_SPEC_FLAGS (conv->prop_spec);
str = g_variant_get_string (wire, NULL);
/* NB: NULL is a safe value for 'str' to be passed into */
ok = bolt_flags_class_from_string (fs->flags_class, str, &v, error);
if (ok)
g_value_set_flags (value, v);
else
g_value_set_flags (value, fs->default_value);
return ok;
}
static GVariant *
conv_obj_to_str (BoltWireConv *conv,
const GValue *value,
GError **error)
{
GVariant *res = NULL;
char *str = NULL;
GObject *obj;
obj = g_value_get_object (value);
if (obj)
g_object_get (obj, "object-id", &str, NULL);
else
str = g_strdup ("");
if (str)
res = g_variant_new_take_string (str);
if (!res)
g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"failed to convert object to string");
return res;
}
static gboolean
conv_obj_from_str (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error)
{
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"failed to convert object from string");
return FALSE;
}
static GVariant *
conv_value_to_variant (BoltWireConv *conv,
const GValue *value,
GError **error)
{
return g_dbus_gvalue_to_gvariant (value, conv->wire_type);
}
static gboolean
conv_value_from_variant (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error)
{
GType want = value->g_type;
g_dbus_gvariant_to_gvalue (wire, value);
/* if the value was initialized before, make sure
* we got the same type */
if (want != 0 && value->g_type != want)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"Can not convert wire from %s to %s",
g_type_name (value->g_type), g_type_name (want));
return FALSE;
}
return TRUE;
}
static GVariant *
conv_str_to_wire (BoltWireConv *conv,
const GValue *value,
GError **error)
{
const char *str = g_value_get_string (value);
if (str == NULL)
str = "";
return g_variant_new_string (str);
}
static gboolean
conv_str_from_wire (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error)
{
const char *str = g_variant_get_string (wire, NULL);
if (str && *str == '\0')
str = NULL;
g_value_set_static_string (value, str);
return TRUE;
}
/* public interfaces */
BoltWireConv *
bolt_wire_conv_ref (BoltWireConv *conv)
{
g_return_val_if_fail (conv != NULL, NULL);
g_return_val_if_fail (conv->ref_count > 0, NULL);
g_atomic_int_inc (&conv->ref_count);
return conv;
}
void
bolt_wire_conv_unref (BoltWireConv *conv)
{
g_return_if_fail (conv != NULL);
g_return_if_fail (conv->ref_count > 0);
if (g_atomic_int_dec_and_test (&conv->ref_count))
{
g_variant_type_free (conv->wire_type);
g_param_spec_unref (conv->prop_spec);
g_clear_pointer (&conv->custom_id, g_free);
g_free (conv);
}
}
const GVariantType *
bolt_wire_conv_get_wire_type (BoltWireConv *conv)
{
g_return_val_if_fail (conv != NULL, NULL);
g_return_val_if_fail (conv->ref_count > 0, NULL);
return conv->wire_type;
}
const GParamSpec *
bolt_wire_conv_get_prop_spec (BoltWireConv *conv)
{
g_return_val_if_fail (conv != NULL, NULL);
g_return_val_if_fail (conv->ref_count > 0, NULL);
return conv->prop_spec;
}
gboolean
bolt_wire_conv_is_native (BoltWireConv *conv)
{
g_return_val_if_fail (conv != NULL, TRUE);
return conv->conv_type == BOLT_WIRE_CONV_NATIVE;
}
const char *
bolt_wire_conv_describe (BoltWireConv *conv)
{
g_return_val_if_fail (conv != NULL, "*invalid*");
switch (conv->conv_type)
{
case BOLT_WIRE_CONV_NATIVE:
return "native";
case BOLT_WIRE_CONV_ENUM_AS_STRING:
return "enum-as-string";
case BOLT_WIRE_CONV_FLAGS_AS_STRING:
return "flags-as-string";
case BOLT_WIRE_CONV_OBJECT_AS_STRING:
return "object-as-string";
case BOLT_WIRE_CONV_CUSTOM:
if (conv->custom_id)
return conv->custom_id;
return "custom";
}
return "*unknown*";
}
BoltWireConv *
bolt_wire_conv_for (const GVariantType *wire_type,
GParamSpec *prop_spec)
{
BoltWireConv *conv;
gboolean as_str;
g_return_val_if_fail (wire_type != NULL, NULL);
g_return_val_if_fail (prop_spec != NULL, NULL);
conv = g_new (BoltWireConv, 1);
conv->ref_count = 1;
conv->wire_type = g_variant_type_copy (wire_type);
conv->prop_spec = g_param_spec_ref (prop_spec);
conv->custom_id = NULL;
as_str = g_variant_type_equal (wire_type, G_VARIANT_TYPE_STRING);
if (as_str && G_IS_PARAM_SPEC_ENUM (prop_spec))
{
conv->conv_type = BOLT_WIRE_CONV_ENUM_AS_STRING;
conv->to_wire = conv_enum_to_str;
conv->from_wire = conv_enum_from_str;
}
else if (as_str && G_IS_PARAM_SPEC_FLAGS (prop_spec))
{
conv->conv_type = BOLT_WIRE_CONV_FLAGS_AS_STRING;
conv->to_wire = conv_flags_to_str;
conv->from_wire = conv_flags_from_str;
}
else if (as_str && G_IS_PARAM_SPEC_OBJECT (prop_spec))
{
conv->conv_type = BOLT_WIRE_CONV_OBJECT_AS_STRING;
conv->to_wire = conv_obj_to_str;
conv->from_wire = conv_obj_from_str;
}
else if (as_str && G_IS_PARAM_SPEC_STRING (prop_spec))
{
conv->conv_type = BOLT_WIRE_CONV_NATIVE;
conv->to_wire = conv_str_to_wire;
conv->from_wire = conv_str_from_wire;
}
else
{
conv->conv_type = BOLT_WIRE_CONV_NATIVE;
conv->to_wire = conv_value_to_variant;
conv->from_wire = conv_value_from_variant;
}
return conv;
}
BoltWireConv *
bolt_wire_conv_custom (const GVariantType *wire_type,
GParamSpec *prop_spec,
const char *custom_id,
BoltConvToWire to_wire,
BoltConvFromWire from_wire)
{
BoltWireConv *conv;
g_return_val_if_fail (wire_type != NULL, NULL);
g_return_val_if_fail (prop_spec != NULL, NULL);
g_return_val_if_fail (wire_type != NULL, NULL);
g_return_val_if_fail (prop_spec != NULL, NULL);
conv = g_new (BoltWireConv, 1);
conv->ref_count = 1;
conv->conv_type = BOLT_WIRE_CONV_CUSTOM;
conv->wire_type = g_variant_type_copy (wire_type);
conv->prop_spec = g_param_spec_ref (prop_spec);
conv->custom_id = g_strdup (custom_id);
conv->to_wire = to_wire;
conv->from_wire = from_wire;
return conv;
}
GVariant *
bolt_wire_conv_to_wire (BoltWireConv *conv,
const GValue *value,
GError **error)
{
GVariant *res = NULL;
g_return_val_if_fail (conv != NULL, NULL);
g_return_val_if_fail (G_IS_VALUE (value), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
res = conv->to_wire (conv, value, error);
if (res == NULL)
return NULL;
if (g_variant_is_floating (res))
g_variant_ref_sink (res);
return res;
}
gboolean
bolt_wire_conv_from_wire (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error)
{
gboolean ok;
g_return_val_if_fail (conv != NULL, FALSE);
g_return_val_if_fail (wire != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
wire_conv_init_value_if_needed (conv, value);
ok = conv->from_wire (conv, wire, value, error);
return ok;
}
bolt-0.9.2/common/bolt-glue.h 0000664 0000000 0000000 00000007145 14174530510 0016024 0 ustar 00root root 0000000 0000000 /*
* Copyright © 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
GParamSpec * bolt_param_spec_override (GObjectClass *object_class,
const char *name);
gboolean bolt_str_parse_by_pspec (GParamSpec *spec,
const char *str,
GValue *val,
GError **error);
GPtrArray * bolt_properties_for_type (GType target);
gboolean bolt_properties_find (GPtrArray *specs,
const char *name,
GParamSpec **spec,
GError **error);
/* wire protocol variant/value conversions */
typedef struct _BoltWireConv BoltWireConv;
typedef GVariant * (*BoltConvToWire) (BoltWireConv *conv,
const GValue *value,
GError **error);
typedef gboolean (*BoltConvFromWire) (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error);
BoltWireConv * bolt_wire_conv_ref (BoltWireConv *conv);
void bolt_wire_conv_unref (BoltWireConv *conv);
const GVariantType * bolt_wire_conv_get_wire_type (BoltWireConv *conv);
const GParamSpec * bolt_wire_conv_get_prop_spec (BoltWireConv *conv);
gboolean bolt_wire_conv_is_native (BoltWireConv *conv);
const char * bolt_wire_conv_describe (BoltWireConv *conv);
BoltWireConv * bolt_wire_conv_for (const GVariantType *wire_type,
GParamSpec *prop_spec);
BoltWireConv * bolt_wire_conv_custom (const GVariantType *wire_type,
GParamSpec *prop_spec,
const char *custom_id,
BoltConvToWire to_wire,
BoltConvFromWire from_wire);
GVariant * bolt_wire_conv_to_wire (BoltWireConv *conv,
const GValue *value,
GError **error);
gboolean bolt_wire_conv_from_wire (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (BoltWireConv, bolt_wire_conv_unref)
#if !GLIB_CHECK_VERSION (2, 57, 1)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GParamSpec, g_param_spec_unref)
#endif
G_END_DECLS
bolt-0.9.2/common/bolt-io.c 0000664 0000000 0000000 00000053616 14174530510 0015476 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#if !HAVE_FN_COPY_FILE_RANGE
#include
#include
# ifndef __NR_copy_file_range
# if defined(__x86_64__)
# define __NR_copy_file_range 326
# elif defined(__i386__)
# define __NR_copy_file_range 377
# else
# error "__NR_copy_file_range on this architecture"
# endif
# endif
#endif
#include "bolt-error.h"
#include "bolt-str.h"
#include "bolt-io.h"
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FILE, fclose);
/* standard open flags for overwriting a file, i.e. close on
* exec (3), open in write only mode, truncate before writing,
* and create it if it does not yet exist */
#define BOLT_O_OVERWRITE (O_CLOEXEC | O_WRONLY | O_TRUNC | O_CREAT)
int
bolt_open (const char *path, int flags, int mode, GError **error)
{
int fd;
g_return_val_if_fail (error == NULL || *error == NULL, -1);
fd = g_open (path, flags, mode);
if (fd < 0)
{
gint code = g_io_error_from_errno (errno);
g_set_error (error, G_IO_ERROR, code,
"could not open '%s': %s",
path, g_strerror (errno));
return -1;
}
return fd;
}
static FILE *
bolt_fdopen (int fd, const char *mode, GError **error)
{
FILE *fp;
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
fp = fdopen (fd, mode);
if (fp == NULL)
{
gint code = g_io_error_from_errno (errno);
g_set_error (error, G_IO_ERROR, code,
"fdopen ('%d') error: %s",
fd, g_strerror (errno));
return NULL;
}
return fp;
}
gboolean
bolt_close (int fd, GError **error)
{
int r;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = close (fd);
if (r == 0)
return TRUE;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"could not close file: %s",
g_strerror (errno));
return FALSE;
}
gboolean
bolt_read_all (int fd,
void *buf,
gsize nbytes,
gsize *nread,
GError **error)
{
char *data = buf;
gsize count = 0;
gboolean ok = TRUE;
g_return_val_if_fail (buf != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
do
{
ssize_t n;
n = read (fd, data, nbytes);
if (n < 0)
{
if (errno == EINTR)
continue;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"read error: %s",
g_strerror (errno));
ok = FALSE;
break;
}
else if (n == 0)
{
break;
}
data += n;
count += n;
nbytes -= n;
}
while (nbytes > 0);
if (nread)
*nread = count;
return ok;
}
gboolean
bolt_write_all (int fd,
const void *buf,
gssize nbytes,
GError **error)
{
const char *data = buf;
gboolean ok = TRUE;
g_return_val_if_fail (buf != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (nbytes < 0)
nbytes = strlen (data);
do
{
ssize_t n;
n = write (fd, data, nbytes);
if (n < 0)
{
if (errno == EINTR)
continue;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"write error: %s",
g_strerror (errno));
ok = FALSE;
}
else if (nbytes > 0 && n == 0)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (EIO),
"write error (zero write)");
ok = FALSE;
}
if (!ok)
break;
data += n;
nbytes -= n;
}
while (nbytes > 0);
return ok;
}
DIR *
bolt_opendir (const char *path,
GError **error)
{
DIR *d = NULL;
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
d = opendir (path);
if (d == NULL)
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"could not open directory ('%s'): %s",
path,
g_strerror (errno));
return NULL;
}
return d;
}
int
bolt_openat (int dirfd, const char *path, int oflag, int mode, GError **error)
{
int fd = -1;
g_return_val_if_fail (path != NULL, -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
fd = openat (dirfd, path, oflag, mode);
if (fd < 0)
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"Could not open file %s: %s",
path,
g_strerror (errno));
}
return fd;
}
DIR *
bolt_opendir_at (int dirfd,
const char *name,
int oflag,
GError **error)
{
int fd = -1;
DIR *cd;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
fd = bolt_openat (dirfd, name, oflag, 0, error);
if (fd < 0)
return NULL;
cd = fdopendir (fd);
if (cd == NULL)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"failed to open directory: %s",
g_strerror (errno));
(void) close (fd);
}
return cd;
}
gboolean
bolt_closedir (DIR *d,
GError **error)
{
int r;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = closedir (d);
if (r < 0)
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"failed close dir: %s",
g_strerror (errno));
return FALSE;
}
return TRUE;
}
gboolean
bolt_mkdirat (int dirfd,
const char *name,
mode_t mode,
GError **error)
{
int r = 0;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = mkdirat (dirfd, name, mode);
if (r < 0)
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"failed to create directory '%s': %s",
name,
g_strerror (errno));
return FALSE;
}
return TRUE;
}
gboolean
bolt_rmdir (const char *name,
GError **error)
{
int r;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = rmdir (name);
if (r < 0)
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"failed to remove directory '%s': %s",
name,
g_strerror (errno));
return FALSE;
}
return TRUE;
}
gboolean
bolt_unlink (const char *name,
GError **error)
{
int r;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = unlink (name);
if (r < 0)
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"failed to unlink '%s': %s",
name,
g_strerror (errno));
return FALSE;
}
return TRUE;
}
gboolean
bolt_unlink_at (int dirfd,
const char *name,
int flag,
GError **error)
{
int r;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = unlinkat (dirfd, name, flag);
if (r < 0)
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"failed to unlink '%s': %s",
name,
g_strerror (errno));
return FALSE;
}
return TRUE;
}
gboolean
bolt_write_file_at (int dirfd,
const char *name,
const char *data,
gssize len,
GError **error)
{
int fd;
gboolean ok;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (data != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
fd = bolt_openat (dirfd, name, BOLT_O_OVERWRITE, 0666, error);
if (fd < 0)
return FALSE;
ok = bolt_write_all (fd, data, len, error);
if (!ok)
(void) close (fd);
else
ok = bolt_close (fd, error);
return ok;
}
char *
bolt_read_value_at (int dirfd,
const char *name,
GError **error)
{
g_autoptr(FILE) fp = NULL;
char line[LINE_MAX], *l;
int fd;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
fd = bolt_openat (dirfd,
name,
O_NOFOLLOW | O_CLOEXEC | O_RDONLY,
0,
error);
if (fd < 0)
return NULL;
fp = bolt_fdopen (fd, "re", error);
if (!fp)
{
g_prefix_error (error, "could not open %s: ", name);
return NULL;
}
l = fgets (line, sizeof (line) - 1, fp);
if (!l)
{
if (ferror (fp))
{
if (errno < 1)
errno = -EIO;
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"io error of file %s: %s",
name,
g_strerror (errno));
return NULL;
}
line[0] = '\0';
}
g_strstrip (line);
return g_strdup (line);
}
gboolean
bolt_write_char_at (int dirfd,
const char *name,
char value,
GError **error)
{
int fd;
ssize_t n;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
fd = bolt_openat (dirfd, name, O_WRONLY | O_CLOEXEC, 0, error);
if (fd < 0)
return FALSE;
retry:
n = write (fd, &value, 1);
if (n == -1)
{
int errsv = errno;
if (errsv == EINTR)
goto retry;
if (errsv == ENOKEY)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_NOKEY,
"device does not contain a key");
}
else if (errsv == EKEYREJECTED)
{
g_set_error_literal (error, BOLT_ERROR, BOLT_ERROR_BADKEY,
"key was rejected");
}
else
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errsv),
"write error: %s",
g_strerror (errno));
}
}
(void) close (fd);
return n > 0;
}
gboolean
bolt_write_int_at (int dirfd,
const char *name,
gint val,
GError **error)
{
char buf[256] = {0, };
gsize n;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
n = g_snprintf (buf, sizeof (buf), "%d", val);
g_assert (n < sizeof (buf));
return bolt_write_file_at (dirfd, name, buf, n, error);
}
gboolean
bolt_read_int_at (int dirfd,
const char *name,
gint *val,
GError **error)
{
g_autofree char *str = NULL;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
str = bolt_read_value_at (dirfd, name, error);
if (str == NULL)
return FALSE;
return bolt_str_parse_as_int (str, val, error);
}
gboolean
bolt_write_uint_at (int dirfd,
const char *name,
guint val,
GError **error)
{
char buf[256] = {0, };
gsize n;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
n = g_snprintf (buf, sizeof (buf), "%u", val);
g_assert (n < sizeof (buf));
return bolt_write_file_at (dirfd, name, buf, n, error);
}
gboolean
bolt_read_uint_at (int dirfd,
const char *name,
guint *val,
GError **error)
{
g_autofree char *str = NULL;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
str = bolt_read_value_at (dirfd, name, error);
if (str == NULL)
return FALSE;
return bolt_str_parse_as_uint (str, val, error);
}
gboolean
bolt_verify_uid (int dirfd,
const char *want,
GError **error)
{
g_autoptr(GError) err = NULL;
g_autofree char *have = NULL;
gsize want_len;
gsize have_len;
gboolean ok;
g_return_val_if_fail (want != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
have = bolt_read_value_at (dirfd, "unique_id", &err);
if (have == NULL)
{
/* make clang's static analyzer happy */
g_return_val_if_fail (err != NULL, FALSE);
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"unique id verification failed: %s",
err->message);
return FALSE;
}
have_len = strlen (have);
want_len = strlen (want);
ok = have_len == want_len && !memcmp (want, have, have_len);
if (!ok)
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"unique id verification failed [%s != %s]",
have, want);
return ok;
}
gboolean
bolt_file_write_all (const char *fn,
const void *data,
gssize n,
GError **error)
{
g_return_val_if_fail (fn != NULL, FALSE);
g_return_val_if_fail (data != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return bolt_write_file_at (AT_FDCWD, fn, data, n, error);
}
gboolean
bolt_ftruncate (int fd,
off_t size,
GError **error)
{
int r;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = ftruncate (fd, size);
if (r == -1)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"could not truncate file: %s",
g_strerror (errno));
return FALSE;
}
return TRUE;
}
int
bolt_mkfifo (const char *path,
mode_t mode,
GError **error)
{
int r;
g_return_val_if_fail (path != NULL, -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
r = mkfifo (path, mode);
if (r == -1)
{
gint code = g_io_error_from_errno (errno);
g_set_error (error, G_IO_ERROR, code,
"could not create FIFO at '%s': %s",
path, g_strerror (errno));
return -1;
}
return r;
}
gboolean
bolt_faddflags (int fd,
int flags,
GError **error)
{
int cur;
int code;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
cur = fcntl (fd, F_GETFL);
if (cur != -1)
{
cur |= flags;
cur = fcntl (fd, F_SETFL, cur);
}
if (cur != -1)
return TRUE;
code = g_io_error_from_errno (errno);
g_set_error (error, G_IO_ERROR, code,
"could not add flags to fd: %s",
g_strerror (errno));
return FALSE;
}
gboolean
bolt_fstat (int fd,
struct stat *statbuf,
GError **error)
{
int code;
int r;
g_return_val_if_fail (statbuf != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = fstat (fd, statbuf);
if (r == 0)
return TRUE;
code = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (code),
"could not stat file: %s",
g_strerror (code));
return FALSE;
}
gboolean
bolt_fstatat (int dirfd,
const char *pathname,
struct stat *statbuf,
int flags,
GError **error)
{
int code;
int r;
g_return_val_if_fail (dirfd > -1, FALSE);
g_return_val_if_fail (pathname != NULL, FALSE);
g_return_val_if_fail (statbuf != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = fstatat (dirfd, pathname, statbuf, flags);
if (r == 0)
return TRUE;
code = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (code),
"could not stat file '%s': %s",
pathname,
g_strerror (code));
return FALSE;
}
gboolean
bolt_fdatasync (int fd,
GError **error)
{
int code;
int r;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = fdatasync (fd);
if (r == 0)
return TRUE;
code = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (code),
"could not sync file data : %s",
g_strerror (code));
return FALSE;
}
gboolean
bolt_lseek (int fd,
off_t offset,
int whence,
int *pos,
GError **error)
{
off_t p;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
p = lseek (fd, offset, whence);
if (p == (off_t) -1)
{
int code = errno;
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (code),
"could not seek file: %s", g_strerror (code));
return FALSE;
}
if (pos)
*pos = p;
return TRUE;
}
gboolean
bolt_rename (const char *from,
const char *to,
GError **error)
{
int code;
int r;
g_return_val_if_fail (from != NULL, FALSE);
g_return_val_if_fail (to != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = rename (from, to);
if (r == 0)
return TRUE;
code = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (code),
"could not rename '%s' to '%s': %s",
from, to, g_strerror (code));
return FALSE;
}
gboolean
bolt_renameat (int from_dir,
const char *from,
int to_dir,
const char *to,
GError **error)
{
int code;
int r;
g_return_val_if_fail (from != NULL, FALSE);
g_return_val_if_fail (to != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
r = renameat (from_dir, from, to_dir, to);
if (r == 0)
return TRUE;
code = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (code),
"could not rename '%s' to '%s': %s",
from, to, g_strerror (code));
return FALSE;
}
#if !HAVE_FN_COPY_FILE_RANGE
static loff_t
copy_file_range (int fd_in,
loff_t *off_in,
int fd_out,
loff_t *off_out,
size_t len,
unsigned int flags)
{
return syscall (__NR_copy_file_range,
fd_in, off_in,
fd_out, off_out,
len,
flags);
}
#endif
gboolean
bolt_copy_bytes (int fd_from,
int fd_to,
size_t len,
GError **error)
{
g_return_val_if_fail (fd_from > -1, FALSE);
g_return_val_if_fail (fd_to > -1, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
do
{
ssize_t r;
r = copy_file_range (fd_from, NULL, fd_to, NULL, len, 0);
if (r == -1)
{
int code = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (code),
"error while copying data: %s",
g_strerror (code));
return FALSE;
}
else if (r == 0)
{
break;
}
len -= r;
}
while (len > 0);
return len == 0;
}
gboolean
bolt_dir_is_empty (DIR *dir,
gboolean *empty,
GError **error)
{
struct dirent *de = NULL;
long n;
g_return_val_if_fail (dir != NULL, FALSE);
g_return_val_if_fail (empty != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
*empty = TRUE;
n = telldir (dir);
if (n == -1)
{
int code = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (code),
"telldir(3) failed: %s",
g_strerror (code));
return FALSE;
}
rewinddir (dir);
while ((de = readdir (dir)) != NULL)
{
if (!g_strcmp0 (de->d_name, ".") ||
!g_strcmp0 (de->d_name, ".."))
continue;
*empty = FALSE;
break;
}
seekdir (dir, n);
return TRUE;
}
/* auto cleanup helpers */
void
bolt_cleanup_close_intpr (int *fd)
{
g_return_if_fail (fd != NULL);
if (*fd > -1)
{
int errsave = errno;
int r;
r = close (*fd);
if (r != 0 && errno == EBADF)
g_warning ("invalid fd passed to auto cleanup");
errno = errsave;
}
}
bolt-0.9.2/common/bolt-io.h 0000664 0000000 0000000 00000014331 14174530510 0015472 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include
#include
#include
#include
#include
#include "bolt-macros.h"
G_BEGIN_DECLS
/* *INDENT-OFF* */
G_DEFINE_AUTOPTR_CLEANUP_FUNC (DIR, closedir);
/* *INDENT-ON* */
int bolt_open (const char *path,
int flags,
int mode,
GError **error);
gboolean bolt_close (int fd,
GError **error);
gboolean bolt_read_all (int fd,
void *buf,
gsize nbytes,
gsize *nread,
GError **error);
gboolean bolt_write_all (int fd,
const void *buf,
gssize nbytes,
GError **error);
gboolean bolt_ftruncate (int fd,
off_t size,
GError **error);
DIR * bolt_opendir (const char *path,
GError **error);
DIR * bolt_opendir_at (int dirfd,
const char *name,
int oflag,
GError **error);
gboolean bolt_closedir (DIR *d,
GError **error);
gboolean bolt_mkdirat (int dirfd,
const char *name,
mode_t mode,
GError **error);
gboolean bolt_rmdir (const char *name,
GError **error);
int bolt_openat (int dirfd,
const char *path,
int oflag,
int mode,
GError **error);
gboolean bolt_unlink (const char *name,
GError **error);
gboolean bolt_unlink_at (int dirfd,
const char *name,
int flag,
GError **error);
gboolean bolt_write_file_at (int dirfd,
const char *name,
const char *data,
gssize len,
GError **error);
char * bolt_read_value_at (int dirfd,
const char *name,
GError **error);
gboolean bolt_write_char_at (int dirfd,
const char *name,
char value,
GError **error);
gboolean bolt_write_int_at (int dirfd,
const char *name,
gint val,
GError **error);
gboolean bolt_read_int_at (int dirfd,
const char *name,
gint *val,
GError **error);
gboolean bolt_write_uint_at (int dirfd,
const char *name,
guint val,
GError **error);
gboolean bolt_read_uint_at (int dirfd,
const char *name,
guint *val,
GError **error);
gboolean bolt_verify_uid (int dirfd,
const char *uid,
GError **error);
gboolean bolt_file_write_all (const char *fn,
const void *data,
gssize n,
GError **error);
int bolt_mkfifo (const char *path,
mode_t mode,
GError **error);
gboolean bolt_faddflags (int fd,
int flags,
GError **error);
gboolean bolt_fstat (int fd,
struct stat *statbuf,
GError **error);
gboolean bolt_fstatat (int dirfd,
const char *pathname,
struct stat *statbuf,
int flags,
GError **error);
gboolean bolt_fdatasync (int fd,
GError **error);
gboolean bolt_lseek (int fd,
off_t offset,
int whence,
int *pos,
GError **error);
gboolean bolt_rename (const char *from,
const char *to,
GError **error);
gboolean bolt_renameat (int from_dir,
const char *from,
int to_dir,
const char *to,
GError **error);
gboolean bolt_copy_bytes (int fd_from,
int fd_to,
size_t len,
GError **error);
gboolean bolt_dir_is_empty (DIR *dir,
gboolean *empty,
GError **error);
/* auto cleanup for I/O handles */
void bolt_cleanup_close_intpr (int *fd);
#define bolt_autoclose bolt_cleanup (bolt_cleanup_close_intpr)
G_END_DECLS
bolt-0.9.2/common/bolt-list.h 0000664 0000000 0000000 00000006651 14174530510 0016044 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include "bolt-macros.h"
G_BEGIN_DECLS
typedef struct BoltList_ BoltList;
struct BoltList_
{
BoltList *next;
BoltList *prev;
};
static inline void
bolt_list_init (BoltList *node)
{
node->next = node;
node->prev = node;
}
static inline void
bolt_list_add_internal (BoltList *prev, BoltList *next, BoltList *node)
{
node->next = next;
node->prev = prev;
next->prev = node;
prev->next = node;
}
static inline BoltList *
bolt_list_add_before (BoltList *pos, BoltList *node)
{
if (pos == NULL)
return node;
bolt_list_add_internal (pos->prev, pos, node);
return pos;
}
static inline BoltList *
bolt_list_add_after (BoltList *pos, BoltList *node)
{
if (pos == NULL)
return node;
bolt_list_add_internal (pos, pos->next, node);
return pos;
}
static inline void
bolt_list_del_internal (BoltList *prev, BoltList *next)
{
prev->next = next;
next->prev = prev;
}
#define bolt_list_entry(node, type, member) bolt_container_of (node, type, member)
/* no head list */
static inline guint
bolt_nhlist_len (BoltList *list)
{
BoltList *i = list;
guint count = 0;
if (list == NULL)
return count;
do
{
count++;
i = i->next;
}
while (i != list);
return count;
}
static inline BoltList *
bolt_nhlist_del (BoltList *list, BoltList *node)
{
if (node == NULL || list == NULL)
return list;
bolt_list_del_internal (node->prev, node->next);
if (node == list)
{
if (list->next == list)
return NULL;
else
return list->next;
}
return list;
}
/*
* Using a BoltList struct an iterator, where:
* next the *current* node
* prev the *head* of the list
*
* The following special conditions are encoded:
* sym | next | prev | state
* S | * | NULL | first iteration, initial state
* E | NULL | * | end of iteration, final state
*/
static inline BoltList *
bolt_nhlist_iter_init (BoltList *iter, BoltList *list)
{
iter->next = list;
iter->prev = NULL;
return iter;
}
#define bolt_nhlist_iter_head(iter_) ((iter_)->prev ? : (iter_)->next)
#define bolt_nhlist_iter_node(iter_) ((iter_ != NULL) ? (iter_)->next : NULL)
#define bolt_nhlist_iter_entry(iter_, type, member) \
bolt_container_of (bolt_nhlist_iter_node (iter_), type, member)
static inline BoltList *
bolt_nhlist_iter_next (BoltList *iter)
{
g_return_val_if_fail (iter != NULL, NULL);
if (iter->next == NULL) /* E */
return NULL;
if (iter->prev == NULL) /* S */
{
iter->prev = iter->next;
return iter->next;
}
iter->next = iter->next->next;
if (iter->next == iter->prev)
iter->next = NULL;
return iter->next;
}
G_END_DECLS
bolt-0.9.2/common/bolt-macros.h 0000664 0000000 0000000 00000003467 14174530510 0016357 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include
#include
G_BEGIN_DECLS
#ifndef offsetof
#define offsetof(type, member) ((size_t) &((type *) 0)->member)
#endif
#define bolt_container_of(ptr, type, member) \
({const typeof (((type *) 0)->member) * p__ = (ptr); \
(type *) ((void *) ((char *) p__ - offsetof (type, member))); })
/* *INDENT-OFF* */
#define bolt_swap(a, b) G_STMT_START { \
typeof (a) t__ = (a); \
(a) = (b); \
(b) = t__; \
} G_STMT_END
#define bolt_steal(ptr, none_value) ({ \
typeof (*(ptr)) t__ = *(ptr); \
*(ptr) = (none_value); \
t__; \
})
/* *INDENT-ON* */
#define bolt_cleanup(x) __attribute__((cleanup (x)))
#if defined(__SANITIZE_ADDRESS__)
# define HAVE_ASAN 1
#elif defined(__has_feature)
# if __has_feature (address_sanitizer)
# define HAVE_ASAN 1
# endif
#endif
#ifndef HAVE_ASAN
# define HAVE_ASAN 0
#endif
G_END_DECLS
bolt-0.9.2/common/bolt-names.c 0000664 0000000 0000000 00000002650 14174530510 0016162 0 ustar 00root root 0000000 0000000 /*
* Copyright © 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-names.h"
/* Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_" */
#define DBUS_OPATH_VALID_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
char *
bolt_gen_object_path (const char *base,
const char *oid)
{
g_autofree char *id = NULL;
if (oid)
{
id = g_strdup (oid);
g_strcanon (id, DBUS_OPATH_VALID_CHARS, '_');
}
if (base && id)
return g_build_path ("/", "/", base, id, NULL);
else if (base)
return g_build_path ("/", "/", base, NULL);
else if (id)
return g_build_path ("/", "/", id, NULL);
return g_strdup ("/");
}
bolt-0.9.2/common/bolt-names.h 0000664 0000000 0000000 00000006146 14174530510 0016173 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
/* D-Bus API revision (here for the lack of a better place) */
#define BOLT_DBUS_API_VERSION 1U
/* logging */
#define BOLT_LOG_DOMAIN_UID "BOLT_DOMAIN_UID"
#define BOLT_LOG_DOMAIN_NAME "BOLT_DOMAIN_NAME"
#define BOLT_LOG_DEVICE_UID "BOLT_DEVICE_UID"
#define BOLT_LOG_DEVICE_NAME "BOLT_DEVICE_NAME"
#define BOLT_LOG_DEVICE_STATE "BOLT_DEVICE_STATE"
#define BOLT_LOG_ERROR_DOMAIN "ERROR_DOMAIN"
#define BOLT_LOG_ERROR_CODE "ERROR_CODE"
#define BOLT_LOG_ERROR_MESSAGE "ERROR_MESSAGE"
#define BOLT_LOG_TOPIC "BOLT_TOPIC"
#define BOLT_LOG_VERSION "BOLT_VERSION"
#define BOLT_LOG_CONTEXT "BOLT_LOG_CONTEXT"
#define BOLT_LOG_BUG_MARK "BOLT_LOG_BUG"
/* logging - message ids */
#define BOLT_LOG_MSG_IDLEN 33
#define BOLT_LOG_MSG_ID_STARTUP "dd11929c788e48bdbb6276fb5f26b08a"
/* dbus */
#define BOLT_DBUS_GRESOURCE_PATH "/bolt/org.freedesktop.bolt.xml"
#define BOLT_DBUS_NAME "org.freedesktop.bolt"
#define BOLT_DBUS_PATH "/org/freedesktop/bolt"
#define BOLT_DBUS_PATH_DOMAINS BOLT_DBUS_PATH "/domains"
#define BOLT_DBUS_PATH_DEVICES BOLT_DBUS_PATH "/devices"
#define BOLT_DBUS_INTERFACE "org.freedesktop.bolt1.Manager"
#define BOLT_DBUS_DEVICE_INTERFACE "org.freedesktop.bolt1.Device"
#define BOLT_DBUS_DOMAIN_INTERFACE "org.freedesktop.bolt1.Domain"
#define BOLT_DBUS_POWER_INTERFACE "org.freedesktop.bolt1.Power"
/* sysfs */
#define BOLT_SYSFS_UNIQUE_ID "unique_id"
#define BOLT_SYSFS_IOMMU "iommu_dma_protection"
#define BOLT_SYSFS_GENERATION "generation"
#define BOLT_SYSFS_RX_LANES "rx_lanes"
#define BOLT_SYSFS_RX_SPEED "rx_speed"
#define BOLT_SYSFS_TX_LANES "tx_lanes"
#define BOLT_SYSFS_TX_SPEED "tx_speed"
#define BOLT_SYSFS_DMI_ID "/sys/class/dmi/id"
#define BOLT_SYSFS_DMI_SYS_VENDOR "sys_vendor"
#define BOLT_SYSFS_DMI_PRODUCT_NAME "product_name"
#define BOLT_SYSFS_DMI_PRODUCT_VERSION "product_version"
/* environment variables */
#define BOLT_ENV_DBPATH "BOLT_DBPATH"
#define BOLT_ENV_RUNTIME_DIRECTORY "RUNTIME_DIRECTORY"
#define BOLT_ENV_STATE_DIRECTORY "STATE_DIRECTORY"
#define BOLT_SD_WATCHDOG_USEC "WATCHDOG_USEC"
#define BOLT_SD_NOTIFY_SOCKET "NOTIFY_SOCKET"
/* other well known names */
#define INTEL_WMI_THUNDERBOLT_GUID "86CCFD48-205E-4A77-9C48-2021CBEDE341"
/* helper functions */
char * bolt_gen_object_path (const char *path_base,
const char *object_id);
G_END_DECLS
bolt-0.9.2/common/bolt-rnd.c 0000664 0000000 0000000 00000005604 14174530510 0015644 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-io.h"
#include "bolt-rnd.h"
#include
#include
#include
#if HAVE_FN_GETRANDOM
#include
# else
# define GRND_NONBLOCK 0
#endif
int
bolt_get_random_data (void *buf, gsize n)
{
gboolean ok;
ok = bolt_random_getrandom (buf, n, GRND_NONBLOCK, NULL);
if (ok)
return BOLT_RNG_GETRANDOM;
ok = bolt_random_urandom (buf, n);
if (ok)
return BOLT_RNG_URANDOM;
bolt_random_prng (buf, n);
return BOLT_RNG_PRNG;
}
/* specific implementations */
gboolean
bolt_random_getrandom (void *buf,
gsize n,
unsigned flags,
GError **error)
{
int r = -1;
g_return_val_if_fail (buf != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
#if HAVE_FN_GETRANDOM
r = getrandom (buf, n, flags);
#else
errno = ENOSYS;
#endif
if (r < 0)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"failed to get random data: %s",
g_strerror (errno));
return FALSE;
}
return TRUE;
}
gboolean
bolt_random_urandom (void *buf, gsize n)
{
gboolean ok;
int rndfd;
gsize len;
g_return_val_if_fail (buf != NULL, FALSE);
rndfd = bolt_open ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY, 0, NULL);
if (rndfd < 0)
return FALSE;
ok = bolt_read_all (rndfd, buf, n, &len, NULL);
(void) close (rndfd);
/* NB: according to the man page random(4), "when calling
read(2) for the device /dev/urandom, reads of up to 256
bytes will return as many bytes as are requested and will
not be interrupted by a signal handler".
*/
return ok && len == n;
}
void
bolt_random_prng (void *buf, gsize n)
{
char *ptr = buf;
const gsize l = n % sizeof (guint32);
const gsize k = n - l;
if (buf == NULL || n == 0)
return;
for (gsize i = 0; i < k; i += sizeof (guint32))
{
guint32 r = g_random_int ();
memcpy (ptr + i, &r, sizeof (guint32));
}
if (l > 0)
{
guint32 r = g_random_int ();
memcpy (ptr + k, &r, l);
}
}
bolt-0.9.2/common/bolt-rnd.h 0000664 0000000 0000000 00000002644 14174530510 0015652 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
/* general function */
typedef enum {
BOLT_RNG_ERROR = -1,
BOLT_RNG_URANDOM = 1,
BOLT_RNG_PRNG = 2,
BOLT_RNG_GETRANDOM = 3,
} BoltRng;
BoltRng bolt_get_random_data (void *buf,
gsize n);
/* specific implementations */
gboolean bolt_random_getrandom (void *buf,
gsize n,
unsigned flags,
GError **error);
gboolean bolt_random_urandom (void *buf,
gsize n);
void bolt_random_prng (void *buf,
gsize n);
G_END_DECLS
bolt-0.9.2/common/bolt-str.c 0000664 0000000 0000000 00000023745 14174530510 0015677 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-error.h"
#include "bolt-macros.h"
#include "bolt-str.h"
#include
#include
#include
typedef void (* zero_fn_t) (void *s,
size_t n);
void
bolt_erase_n (void *data, gsize n)
{
#if !HAVE_FN_EXPLICIT_BZERO
#warning no explicit bzero, using fallback
static volatile zero_fn_t explicit_bzero = bzero;
#endif
explicit_bzero (data, n);
}
void
bolt_str_erase (char *str)
{
if (str == NULL)
return;
bolt_erase_n (str, strlen (str));
}
void
bolt_str_erase_clear (char **str)
{
g_return_if_fail (str != NULL);
if (*str == NULL)
return;
bolt_str_erase (*str);
g_free (*str);
*str = NULL;
}
GStrv
bolt_strv_from_ptr_array (GPtrArray **array)
{
GPtrArray *a;
if (array == NULL || *array == NULL)
return NULL;
a = *array;
if (a->len == 0 || a->pdata[a->len - 1] != NULL)
g_ptr_array_add (a, NULL);
*array = NULL;
return (GStrv) g_ptr_array_free (a, FALSE);
}
GStrv
bolt_strv_make_n (guint size, const char *init)
{
GPtrArray *a;
a = g_ptr_array_sized_new (size + 1);
for (guint i = 0; i < size; i++)
g_ptr_array_add (a, g_strdup (init));
return bolt_strv_from_ptr_array (&a);
}
gsize
bolt_strv_length (char * const *strv)
{
gsize l = 0;
if (strv == NULL)
return 0;
while (*strv++ != NULL)
l++;
return l;
}
guint
bolt_gstrv_length0 (const GStrv strv)
{
if (strv == NULL)
return 0;
return g_strv_length ((char **) strv);
}
char **
bolt_strv_contains (GStrv haystack, const char *needle)
{
g_return_val_if_fail (needle != NULL, NULL);
if (haystack == NULL)
return NULL;
for (char **iter = haystack; *iter != NULL; iter++)
if (bolt_streq (*iter, needle))
return iter;
return NULL;
}
gboolean
bolt_strv_equal (const GStrv a, const GStrv b)
{
guint na, nb;
if (a == b)
return TRUE;
na = a == NULL ? 0 : g_strv_length (a);
nb = b == NULL ? 0 : g_strv_length (b);
if (na != nb)
return FALSE;
for (guint i = 0; i < na; i++)
if (!bolt_streq (a[i], b[i]))
return FALSE;
return TRUE;
}
GHashTable *
bolt_strv_diff (const GStrv before,
const GStrv after)
{
GHashTable *diff = NULL;
diff = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
/* build an index of everything in 'before', marking is as
* deleted for now
*/
for (char **iter = before; iter && *iter; iter++)
{
char *key = *iter;
if (bolt_strzero (key))
continue;
g_hash_table_insert (diff, g_strdup (key), GINT_TO_POINTER ('-'));
}
for (char **iter = after; iter && *iter; iter++)
{
char *key = *iter;
gboolean have;
if (bolt_strzero (*iter))
continue;
have = g_hash_table_contains (diff, key);
/* if *iter is also in before, we have it in both GStrv and
* thus we remove it from the diff; otherwise it is a new
* string and we add it to the diff
*/
if (have)
g_hash_table_remove (diff, key);
else
g_hash_table_insert (diff, g_strdup (key),
GINT_TO_POINTER ('+'));
}
return diff;
}
char **
bolt_strv_rotate_left (char **strv)
{
char *start;
char **prev;
char **iter;
if (strv == NULL || *strv == NULL)
return NULL;
start = *strv; /* remember the first element */
for (prev = strv, iter = strv + 1; *iter; prev = iter, iter++)
*prev = *iter;
*prev = start;
return prev;
}
void
bolt_strv_permute (char **strv)
{
guint n;
if (strv == NULL || *strv == NULL)
return;
n = bolt_strv_length (strv);
g_return_if_fail (n > 0);
/* Knuth shuffles */
for (guint i = 0; i < (n - 1) && strv[i]; i++)
{
/* random int k, with i ≤ k < n,
* i.e. random_int_range returns k ∈ [i, n-1] */
gsize k = g_random_int_range (i, n);
bolt_swap (strv[i], strv[k]);
}
}
gboolean
bolt_uuidv_check (const GStrv uuidv,
gboolean empty_ok,
GError **error)
{
if (bolt_strv_isempty (uuidv))
{
if (empty_ok)
return TRUE;
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"provided uuid array is empty");
return FALSE;
}
for (char * const *p = uuidv; *p; p++)
{
const char *uuid = *p;
if (bolt_strzero (uuid) && empty_ok)
continue;
if (!g_uuid_string_is_valid (uuid))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"entry '%s' is not a valid UUID", uuid);
return FALSE;
}
}
return TRUE;
}
char *
bolt_strdup_validate (const char *string)
{
g_autofree char *str = NULL;
gboolean ok;
gsize l;
if (string == NULL)
return NULL;
str = g_strdup (string);
str = g_strstrip (str);
l = strlen (str);
if (l == 0)
return NULL;
ok = g_utf8_validate (str, l, NULL);
if (!ok)
return NULL;
return g_steal_pointer (&str);
}
char *
bolt_strstrip (char *string)
{
char *str;
if (string == NULL)
return NULL;
str = g_strstrip (string);
if (strlen (str) == 0)
g_clear_pointer (&str, g_free);
return str;
}
gboolean
bolt_str_parse_as_int (const char *str,
gint *ret,
GError **error)
{
char *end;
gint64 val;
g_return_val_if_fail (str != NULL, -1);
errno = 0;
val = g_ascii_strtoll (str, &end, 0);
if (val == 0 && (errno != 0 || str == end))
{
if (errno == 0)
errno = EINVAL;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"failed to parse '%s' as unsigned integer", str);
return FALSE;
}
else if (((val == G_MAXINT64 || val == G_MININT64) && errno == ERANGE) ||
val > G_MAXINT || val < G_MININT)
{
if (errno == 0)
errno = ERANGE;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"parsing '%s' overflows unsigned integer", str);
return FALSE;
}
if (ret)
*ret = (gint) val;
return TRUE;
}
gboolean
bolt_str_parse_as_uint (const char *str,
guint *ret,
GError **error)
{
guint64 val;
gboolean ok;
ok = bolt_str_parse_as_uint64 (str, &val, error);
if (!ok)
return FALSE;
if (val > G_MAXUINT)
{
errno = ERANGE;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"parsing '%s' overflows uint", str);
return FALSE;
}
if (ret)
*ret = (guint) val;
return TRUE;
}
gboolean
bolt_str_parse_as_uint64 (const char *str,
guint64 *ret,
GError **error)
{
guint64 val;
char *end;
g_return_val_if_fail (str != NULL, FALSE);
errno = 0;
val = g_ascii_strtoull (str, &end, 0);
if (val == 0 && (errno != 0 || str == end))
{
if (errno == 0)
errno = EINVAL;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"failed to parse '%s' as unsigned integer", str);
return FALSE;
}
else if (val == G_MAXUINT64 && errno == ERANGE)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"parsing '%s' overflows unsigned integer", str);
return FALSE;
}
if (ret)
*ret = val;
return TRUE;
}
gboolean
bolt_str_parse_as_uint32 (const char *str,
guint32 *ret,
GError **error)
{
gboolean ok;
guint64 val;
ok = bolt_str_parse_as_uint64 (str, &val, error);
if (!ok)
return FALSE;
if (val > G_MAXUINT32)
{
errno = ERANGE;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"parsing '%s' overflows uint32", str);
return FALSE;
}
if (ret)
*ret = (guint32) val;
return TRUE;
}
gboolean
bolt_str_parse_as_boolean (const char *str,
gboolean *ret,
GError **error)
{
struct
{
const char *s;
gboolean v;
} table[] = {
{"1", TRUE}, {"0", FALSE},
/* true, false */
{"t", TRUE}, {"f", FALSE},
{"true", TRUE}, {"false", FALSE},
/* yes, no */
{"y", TRUE}, {"n", FALSE},
{"yes", TRUE}, {"no", FALSE},
/* on, off */
{"on", TRUE}, {"off", FALSE},
};
g_return_val_if_fail (str != NULL, FALSE);
for (gsize i = 0; i < G_N_ELEMENTS (table); i++)
{
if (bolt_strcaseeq (table[i].s, str))
{
if (ret)
*ret = table[i].v;
return TRUE;
}
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"failed to parse '%s' as boolean", str);
return FALSE;
}
gboolean
bolt_set_strdup_printf (char **target,
const char *fmt,
...)
{
va_list args;
char *str;
g_return_val_if_fail (target != NULL, FALSE);
g_return_val_if_fail (fmt != NULL, FALSE);
va_start (args, fmt);
str = g_strdup_vprintf (fmt, args);
va_end (args);
return bolt_set_str (target, str);
}
gint
bolt_comparefn_strcmp (gconstpointer a,
gconstpointer b)
{
const char * const *astrv = a;
const char * const *bstrv = b;
return g_strcmp0 (*astrv, *bstrv);
}
bolt-0.9.2/common/bolt-str.h 0000664 0000000 0000000 00000007045 14174530510 0015677 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include
G_BEGIN_DECLS
void bolt_erase_n (void *data,
gsize n);
void bolt_str_erase (char *str);
void bolt_str_erase_clear (char **str);
#define bolt_streq(s1, s2) (g_strcmp0 ((s1), (s2)) == 0)
#define bolt_strcaseeq(s1, s2) (g_ascii_strcasecmp ((s1), (s2)) == 0)
GStrv bolt_strv_from_ptr_array (GPtrArray **array);
GStrv bolt_strv_make_n (guint size,
const char *init);
gsize bolt_strv_length (char * const *strv);
guint bolt_gstrv_length0 (const GStrv strv);
char ** bolt_strv_contains (GStrv haystack,
const char *needle);
gboolean bolt_strv_equal (const GStrv a,
const GStrv b);
GHashTable * bolt_strv_diff (const GStrv before,
const GStrv after);
char ** bolt_strv_rotate_left (char **strv);
void bolt_strv_permute (char **strv);
#define bolt_strv_isempty(strv) ((strv) == NULL || *(strv) == NULL)
gboolean bolt_uuidv_check (const GStrv uuidv,
gboolean empty_ok,
GError **error);
#define bolt_strzero(str) (str == NULL || *str == '\0')
#define bolt_yesno(val) val ? "yes" : "no"
#define bolt_okfail(val) (!!val) ? "ok" : "fail"
char *bolt_strdup_validate (const char *string);
char *bolt_strstrip (char *string);
gboolean bolt_str_parse_as_int (const char *str,
gint *ret,
GError **error);
gboolean bolt_str_parse_as_uint (const char *str,
guint *ret,
GError **error);
gboolean bolt_str_parse_as_uint64 (const char *str,
guint64 *ret,
GError **error);
gboolean bolt_str_parse_as_uint32 (const char *str,
guint32 *ret,
GError **error);
gboolean bolt_str_parse_as_boolean (const char *str,
gboolean *ret,
GError **error);
/* replacing string pointers, like g_set_object, g_set_error ... */
static inline gboolean
bolt_set_str (char **target, char *str)
{
char *ptr;
g_return_val_if_fail (target != NULL, FALSE);
ptr = *target;
if (ptr == str)
return FALSE;
g_free (ptr);
*target = str;
return TRUE;
}
#define bolt_set_strdup(target, str) \
bolt_set_str (target, g_strdup (str))
gboolean bolt_set_strdup_printf (char **target,
const char *fmt,
...) G_GNUC_PRINTF (2, 3);
gint bolt_comparefn_strcmp (gconstpointer a,
gconstpointer b);
G_END_DECLS
bolt-0.9.2/common/bolt-term.c 0000664 0000000 0000000 00000005326 14174530510 0016031 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-str.h"
#include "bolt-term.h"
#include
#include
int
bolt_is_fancy_terminal (void)
{
const char *val;
if (!isatty (STDOUT_FILENO))
return 0;
val = g_getenv ("TERM");
if (val && !g_ascii_strcasecmp (val, "dumb"))
return 0;
return 1;
}
const char *
bolt_color (const char *color)
{
static gint on = -1;
if (G_UNLIKELY (on == -1))
on = bolt_is_fancy_terminal ();
return on ? color : "";
}
/* borrowed from systemd */
static const char *const ansi_glphys[BOLT_GLYPH_LAST] = {
[TREE_VERTICAL] = "| ",
[TREE_BRANCH] = "|-",
[TREE_RIGHT] = "`-",
[TREE_SPACE] = " ",
[WHITE_CIRCLE] = "o",
[BLACK_CIRCLE] = "*",
[ARROW] = "->",
[MDASH] = "-",
[ELLIPSIS] = "...",
[WARNING_SIGN] = "!",
};
static const char *const utf8_glphys[BOLT_GLYPH_LAST] = {
[TREE_VERTICAL] = "\342\224\202 ", /* │ */
[TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
[TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
[TREE_SPACE] = " ", /* */
[WHITE_CIRCLE] = "\342\227\213", /* ○ */
[BLACK_CIRCLE] = "\342\227\217", /* ● */
[ARROW] = "\342\206\222", /* → */
[MDASH] = "\342\200\223", /* – */
[ELLIPSIS] = "\342\200\246", /* … */
[WARNING_SIGN] = "\342\232\240", /* ⚠ */
};
const char *
bolt_glyph (BoltGlyph g)
{
static const char *const *glyph_table = NULL;
g_return_val_if_fail (g < BOLT_GLYPH_LAST, "?");
/* TODO: check for utf-8 support */
if (G_UNLIKELY (glyph_table == NULL))
{
if (bolt_is_fancy_terminal ())
glyph_table = utf8_glphys;
else
glyph_table = ansi_glphys;
}
return glyph_table[g];
}
bolt-0.9.2/common/bolt-term.h 0000664 0000000 0000000 00000002633 14174530510 0016034 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include
G_BEGIN_DECLS
#define ANSI_NORMAL "\x1B[0m"
#define ANSI_RED "\x1B[0;31m"
#define ANSI_GREEN "\x1B[0;32m"
#define ANSI_YELLOW "\x1B[0;33m"
#define ANSI_BLUE "\x1B[0;34m"
#define ANSI_HIGHLIGHT_BLACK "\x1B[0;1;30m"
#define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m"
int bolt_is_fancy_terminal (void);
const char * bolt_color (const char *color);
typedef enum {
TREE_VERTICAL,
TREE_BRANCH,
TREE_RIGHT,
TREE_SPACE,
BLACK_CIRCLE,
WHITE_CIRCLE,
ARROW,
MDASH,
ELLIPSIS,
WARNING_SIGN,
BOLT_GLYPH_LAST
} BoltGlyph;
const char * bolt_glyph (BoltGlyph g);
G_END_DECLS
bolt-0.9.2/common/bolt-time.c 0000664 0000000 0000000 00000002315 14174530510 0016013 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-time.h"
char *
bolt_epoch_format (guint64 seconds, const char *format)
{
g_autoptr(GDateTime) dt = NULL;
g_return_val_if_fail (format != NULL, NULL);
dt = g_date_time_new_from_unix_utc ((gint64) seconds);
if (dt == NULL)
return NULL;
return g_date_time_format (dt, format);
}
guint64
bolt_now_in_seconds (void)
{
gint64 now = g_get_real_time ();
return (guint64) now / G_USEC_PER_SEC;
}
bolt-0.9.2/common/bolt-time.h 0000664 0000000 0000000 00000002204 14174530510 0016015 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
G_BEGIN_DECLS
#define BOLT_MSEC_PER_SEC 1000LL /* number of milli-seconds (ms) in a second (s) */
#define BOLT_USEC_PER_MSEC 1000LL /* number of micro-seconds (µs) in a ms */
char * bolt_epoch_format (guint64 seconds,
const char *format);
guint64 bolt_now_in_seconds (void);
G_END_DECLS
bolt-0.9.2/common/bolt-unix.c 0000664 0000000 0000000 00000010015 14174530510 0016034 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-unix.h"
#include "bolt-error.h"
#include "bolt-io.h"
#include "bolt-names.h"
#include "bolt-str.h"
#include
#include
#include
#include
#include
gboolean
bolt_pid_is_alive (pid_t pid)
{
gulong p = (gulong) pid;
char path[256];
if (pid != 0)
g_snprintf (path, sizeof (path), "/proc/%lu/stat", p);
else
g_snprintf (path, sizeof (path), "/proc/self/stat");
return g_file_test (path, G_FILE_TEST_EXISTS);
}
gboolean
bolt_sd_notify_literal (const char *state,
gboolean *sent,
GError **error)
{
bolt_autoclose int fd = -1;
struct sockaddr_un sau = {AF_UNIX, };
struct msghdr msghdr = { NULL, };
struct iovec iovec = { };
const char *env;
socklen_t socklen;
size_t len;
ssize_t r;
g_return_val_if_fail (state != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (sent)
*sent = FALSE;
env = g_getenv (BOLT_SD_NOTIFY_SOCKET);
if (env == NULL)
return TRUE;
len = strlen (env);
if (len > sizeof (sau.sun_path) - 1)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"unix domain socket path too long: %s", env);
return FALSE;
}
socklen = offsetof (struct sockaddr_un, sun_path) + len;
switch (*env)
{
case '@':
/* abstract sockets have sun_path[0] set to '\0',
* otherwise '\0' have no special meaning, i.e.
* the address is not null-terminted */
memcpy (sau.sun_path + 1, env + 1, len);
socklen += 1; /* the leading '\0' */
break;
case '/':
/* pathname: null-terminated filesystem path */
memcpy (sau.sun_path, env, len + 1);
break;
default:
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"unsupported socket address: %s", env);
return FALSE;
}
fd = socket (AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (fd < 0)
return bolt_error_for_errno (error, errno, "failed to open socket %m");
iovec.iov_base = (void *) state;
iovec.iov_len = strlen (state);
msghdr.msg_iov = &iovec;
msghdr.msg_iovlen = 1;
msghdr.msg_name = &sau;
msghdr.msg_namelen = socklen;
r = sendmsg (fd, &msghdr, MSG_NOSIGNAL);
if (sent)
*sent = TRUE;
if (r < 0)
{
bolt_error_for_errno (error, errno, "failed to send msg: %m");
return FALSE;
}
else if ((size_t) r != iovec.iov_len)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
"failed to send complete message: %s", env);
return FALSE;
}
return TRUE;
}
int
bolt_sd_watchdog_enabled (guint64 *timeout,
GError **error)
{
const char *str;
guint64 val = 0;
gboolean ok;
str = g_getenv (BOLT_SD_WATCHDOG_USEC);
if (str == NULL)
return 0;
ok = bolt_str_parse_as_uint64 (str, &val, error);
if (ok && (val == 0 || val == (guint64) - 1))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"invalid value '%" G_GUINT64_FORMAT "'", val);
ok = FALSE;
}
if (!ok)
g_prefix_error (error, "failed to parse WATCHDOG_USEC: ");
else if (timeout)
*timeout = val;
return ok ? 1 : -1;
}
bolt-0.9.2/common/bolt-unix.h 0000664 0000000 0000000 00000002246 14174530510 0016050 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include
G_BEGIN_DECLS
gboolean bolt_pid_is_alive (pid_t pid);
gboolean bolt_sd_notify_literal (const char *state,
gboolean *sent,
GError **error);
int bolt_sd_watchdog_enabled (guint64 *timeout,
GError **error);
G_END_DECLS
bolt-0.9.2/common/bolt-wire.c 0000664 0000000 0000000 00000005566 14174530510 0016036 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2020 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#include "config.h"
#include "bolt-wire.h"
#include "bolt-error.h"
#include
G_DEFINE_BOXED_TYPE (BoltLinkSpeed, bolt_link_speed, bolt_link_speed_copy, g_free);
BoltLinkSpeed *
bolt_link_speed_copy (const BoltLinkSpeed *other)
{
BoltLinkSpeed *copy = g_new (BoltLinkSpeed, 1);
*copy = *other;
return copy;
}
gboolean
bolt_link_speed_equal (const BoltLinkSpeed *a,
const BoltLinkSpeed *b)
{
return
a->rx.speed == b->rx.speed &&
a->rx.lanes == b->rx.lanes &&
a->tx.speed == b->tx.speed &&
a->tx.lanes == b->tx.lanes;
}
GVariant *
bolt_link_speed_to_wire (BoltWireConv *conv,
const GValue *value,
GError **error)
{
GVariantBuilder builder;
BoltLinkSpeed *link;
link = g_value_get_boxed (value);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{su}"));
g_variant_builder_add (&builder, "{su}", "rx.speed", link->rx.speed);
g_variant_builder_add (&builder, "{su}", "rx.lanes", link->rx.lanes);
g_variant_builder_add (&builder, "{su}", "tx.speed", link->tx.speed);
g_variant_builder_add (&builder, "{su}", "tx.lanes", link->tx.lanes);
return g_variant_builder_end (&builder);
}
gboolean
bolt_link_speed_from_wire (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error)
{
BoltLinkSpeed link;
gboolean ok;
struct
{
const char *name;
guint32 *target;
} entries[] = {
{"rx.speed", &link.rx.speed},
{"rx.lanes", &link.rx.lanes},
{"tx.speed", &link.tx.speed},
{"tx.lanes", &link.tx.lanes},
};
for (unsigned i = 0; i < G_N_ELEMENTS (entries); i++)
{
const char *name = entries[i].name;
guint32 *target = entries[i].target;
ok = g_variant_lookup (wire, name, "u", target);
if (!ok)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"Missing entry in LinkSpeed dict: %s",
name);
return FALSE;
}
}
g_value_set_boxed (value, &link);
return ok;
}
bolt-0.9.2/common/bolt-wire.h 0000664 0000000 0000000 00000003470 14174530510 0016033 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2020 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
#include
#include
G_BEGIN_DECLS
typedef struct BoltLinkSpeed_ BoltLinkSpeed;
struct BoltLinkSpeed_
{
struct
{
struct
{
guint32 speed;
guint32 lanes;
} rx;
struct
{
guint32 speed;
guint32 lanes;
} tx;
};
};
GType bolt_link_speed_get_type (void);
#define BOLT_TYPE_LINK_SPEED (bolt_link_speed_get_type ())
BoltLinkSpeed * bolt_link_speed_copy (const BoltLinkSpeed *ls);
gboolean bolt_link_speed_equal (const BoltLinkSpeed *a,
const BoltLinkSpeed *b);
GVariant * bolt_link_speed_to_wire (BoltWireConv *conv,
const GValue *value,
GError **error);
gboolean bolt_link_speed_from_wire (BoltWireConv *conv,
GVariant *wire,
GValue *value,
GError **error);
G_END_DECLS
bolt-0.9.2/common/fix-coverity.h 0000664 0000000 0000000 00000006153 14174530510 0016560 0 ustar 00root root 0000000 0000000 /*
* fix-coverity.h
* Copyright 2017 Peter Jones
*
* Distributed under terms of the LGPL 2.1+ license.
*/
#ifndef FIX_COVERITY_H
#define FIX_COVERITY_H
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __COVERITY_GCC_VERSION_AT_LEAST
#define __COVERITY_GCC_VERSION_AT_LEAST(x, y) 0
#define FAKE__COVERITY_GCC_VERSION_AT_LEAST__
#endif /* __COVERITY_GCC_VERSION_AT_LEAST */
/* With gcc 7 on x86_64 (at least), coverity pretends to be GCC but
* accidentally doesn't create all of the types GCC would.
*
* In glibc's headers, bits/floatn.h has:
*
* #if (defined __x86_64__ \
* ? __GNUC_PREREQ (4, 3) \
* : (defined __GNU__ ? __GNUC_PREREQ (4, 5) : __GNUC_PREREQ (4, 4)))
* # define __HAVE_FLOAT128 1
* #else
* # define __HAVE_FLOAT128 0
* #endif
*
* and stdlib.h has:
*
* #if __HAVE_FLOAT128 && __GLIBC_USE (IEC_60559_TYPES_EXT)
* slash* Likewise for the '_Float128' format *slash
* extern _Float128 strtof128 (const char *__restrict __nptr,
* char **__restrict __endptr)
* __THROW __nonnull ((1));
* #endif
*
* Which then causes cov-emit to lose its shit:
*
* "/usr/include/stdlib.h", line 133: error #20: identifier "_Float128" is
* undefined
* extern _Float128 strtof128 (const char *__restrict __nptr,
* ^
* "/usr/include/stdlib.h", line 190: error #20: identifier "_Float128" is
* undefined
* _Float128 __f)
* ^
* "/usr/include/stdlib.h", line 236: error #20: identifier "_Float128" is
* undefined
* extern _Float128 strtof128_l (const char *__restrict __nptr,
* ^
*
* And then you'll notice something like this later on:
* [WARNING] Emitted 0 C/C++ compilation units (0%) successfully
*
* 0 C/C++ compilation units (0%) are ready for analysis
* For more details, please look at:
* /home/pjones/devel/github.com/dbxtool/master/cov-int/build-log.txt
*
* You would think that if you're writing something that pretends to be
* gcc, and you've got a "build a configuration by running shit through gcc
* and looking at the output" stage (which they do), you would run "gcc -da
* -fdump-tree-all -c -o foo.o foo.c" on an empty file and snarf up all the
* types defined in the foo.c.001t.tu output. Apparently, they do not.
*
* Anyway, even just defining the type doesn't always work in the face of
* how _Complex is defined, so we cheat a bit here. Be prepared to vomit.
*/
#ifdef __x86_64__
#if __COVERITY_GCC_VERSION_AT_LEAST (7, 0)
#if 0
typedef float _Float128 __attribute__((__mode__ (__TF__)));
typedef __complex__ float __cfloat128 __attribute__((__mode__ (__TC__)));
typedef _Complex float __cfloat128 __attribute__((__mode__ (__TC__)));
#else
#include
#define __cplusplus 201103L
#include
#undef __cplusplus
#endif
#endif
#endif
#ifdef FAKE__COVERITY_GCC_VERSION_AT_LEAST__
#undef FAKE__COVERITY_GCC_VERSION_AT_LEAST
#undef __COVERITY_GCC_VERSION_AT_LEAST
#endif
#endif /* !FIX_COVERITY_H */
// vim:fenc=utf-8:tw=75
bolt-0.9.2/config.h.in 0000664 0000000 0000000 00000003073 14174530510 0014510 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2018 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
#pragma once
/* naming and versioning */
#mesondefine PACKAGE_NAME
#mesondefine PACKAGE_VERSION
#mesondefine VERSION
#mesondefine VERSION_MAJOR
#mesondefine VERSION_MINOR
/* directories */
#mesondefine DATADIR
#mesondefine LOCALSTATEDIR
#mesondefine BOLT_DBNAME
#mesondefine BOLT_DBDIR
#mesondefine DATADIR
/* availability of features */
#mesondefine HAVE_FN_EXPLICIT_BZERO
#mesondefine HAVE_FN_GETRANDOM
#mesondefine HAVE_FN_COPY_FILE_RANGE
#mesondefine HAVE_POLKIT_AUTOPTR
/* constants */
#mesondefine _GNU_SOURCE
/* fixup coverity build issues */
#mesondefine IS_COVERITY_BUILD
#ifdef IS_COVERITY_BUILD
#include "fix-coverity.h"
#endif
/* use structure logging f */
#define G_LOG_USE_STRUCTURED 1
/* glib version checking */
#mesondefine GLIB_VERSION_MIN_REQUIRED
#mesondefine GLIB_VERSION_MAX_ALLOWED
bolt-0.9.2/contrib/ 0000775 0000000 0000000 00000000000 14174530510 0014122 5 ustar 00root root 0000000 0000000 bolt-0.9.2/contrib/Dockerfile-alpine 0000664 0000000 0000000 00000000372 14174530510 0017364 0 ustar 00root root 0000000 0000000 ## -*- mode: dockerfile -*-
FROM alpine:latest
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN apk add --no-cache bash dbus dbus-dev eudev-dev gcc glib-dev libc-dev meson ninja polkit-dev udev
RUN mkdir /src /build
WORKDIR /src
bolt-0.9.2/contrib/Dockerfile-arch 0000664 0000000 0000000 00000000756 14174530510 0017037 0 ustar 00root root 0000000 0000000 ## -*- mode: dockerfile -*-
FROM archlinux/archlinux:base-devel
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8
RUN pacman -Syu --noconfirm
RUN pacman -S --noconfirm base-devel
RUN pacman -S --noconfirm \
asciidoc \
dbus-glib \
git \
gobject-introspection \
gtk-doc \
meson \
perl-sgmls \
polkit \
python-dbus \
python-gobject \
python-pip \
umockdev \
valgrind
RUN pip3 install python-dbusmock pylint==2.4.1
RUN mkdir /src /build
WORKDIR /src
bolt-0.9.2/contrib/Dockerfile-coverity 0000664 0000000 0000000 00000001750 14174530510 0017761 0 ustar 00root root 0000000 0000000 ## -*- mode: dockerfile -*-
FROM fedora:34
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN dnf --enablerepo=updates-testing -y update
RUN dnf --enablerepo=updates-testing -y install \
clang-analyzer \
codespell \
gcc \
git \
glib2-devel \
glibc-langpack-en \
gtk-doc \
lcov \
libgudev-devel \
meson-0.56.2-2.fc34 \
polkit-devel \
python3 \
python3-dbus \
python3-dbusmock \
python3-gobject \
rpm-build \
redhat-rpm-config \
systemd-devel \
umockdev-devel \
uncrustify \
wget
ARG TOKEN
ARG PROJECT
ARG ORG
ENV HOME "/root"
WORKDIR "$HOME"
RUN curl https://scan.coverity.com/download/linux64 \
--form project=${ORG}/${PROJECT} \
--form token=${TOKEN} \
-o coverity_tool.tgz && \
tar zxf coverity_tool.tgz \
&& rm coverity_tool.tgz && \
mv cov-analysis-linux64-* cov-analysis-linux64
ENV PATH "$PATH:$HOME/cov-analysis-linux64/bin"
RUN mkdir /src /build
WORKDIR /src
bolt-0.9.2/contrib/Dockerfile-debian 0000664 0000000 0000000 00000000735 14174530510 0017341 0 ustar 00root root 0000000 0000000 ## -*- mode: dockerfile -*-
FROM debian:sid
RUN apt-get update -q && apt-get install -y \
asciidoc \
git \
gobject-introspection \
libgirepository1.0-dev \
libpolkit-gobject-1-dev \
locales \
libudev-dev \
libumockdev-dev \
libsystemd-dev \
meson \
pkg-config \
policykit-1 \
python3-dbus \
python3-dbusmock \
udev \
umockdev \
systemd \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir /src /build
WORKDIR /src
bolt-0.9.2/contrib/Dockerfile-fedora 0000664 0000000 0000000 00000001136 14174530510 0017353 0 ustar 00root root 0000000 0000000 ## -*- mode: dockerfile -*-
FROM fedora:34
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN dnf --enablerepo=updates-testing -y update
RUN dnf --enablerepo=updates-testing -y install \
clang-analyzer \
codespell \
gcc \
git \
glib2-devel \
glibc-langpack-en \
gtk-doc \
lcov \
libgudev-devel \
meson-0.56.2-2.fc34 \
polkit-devel \
python3 \
python3-dbus \
python3-dbusmock \
python3-gobject \
rpm-build \
redhat-rpm-config \
systemd-devel \
umockdev-devel \
uncrustify
RUN mkdir /src /build
WORKDIR /src
bolt-0.9.2/contrib/Dockerfile-ub1804 0000664 0000000 0000000 00000000774 14174530510 0017045 0 ustar 00root root 0000000 0000000 ## -*- mode: dockerfile -*-
FROM ubuntu:18.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update -q && apt-get install -yy \
asciidoc \
git \
gobject-introspection \
libgirepository1.0-dev \
libpolkit-gobject-1-dev \
locales \
libudev-dev \
libumockdev-dev \
libsystemd-dev \
meson \
pkg-config \
policykit-1 \
python3-dbus \
python3-dbusmock \
udev \
umockdev \
systemd \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir /src
WORKDIR /src
bolt-0.9.2/contrib/PKGBUILD 0000664 0000000 0000000 00000001433 14174530510 0015247 0 ustar 00root root 0000000 0000000 pkgname=bolt-git
pkgver=r271.6d86460
pkgrel=1
pkgdesc="Thunderbolt 3 security system daemon"
arch=('i686' 'x86_64')
url="https://github.com/gicmo/bolt"
license=('LGPL')
depends=('polkit' 'systemd')
makedepends=('asciidoc' 'meson' 'git')
checkdepends=('umockdev')
conflicts=('bolt')
provides=('bolt')
source=(git+https://github.com/gicmo/bolt.git)
md5sums=('SKIP')
pkgver() {
cd bolt
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}
build() {
cd "${srcdir}"
install -d build
arch-meson bolt build
ninja -v -C build
}
check() {
cd "${srcdir}"
ninja -C build test
}
package() {
cd "${srcdir}"
DESTDIR="${pkgdir}" ninja -C build install
# Fixup mode to match polkit
install -d -o root -g 102 -m 750 "${pkgdir}/usr/share/polkit-1/rules.d"
}
bolt-0.9.2/contrib/bolt-mock 0000775 0000000 0000000 00000053072 14174530510 0015746 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# boltd mocking
#
# Copyright © 2017 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 .
# Authors:
# Christian J. Kellner
from __future__ import print_function
import argparse
import atexit
import os
import readline
import shlex
import shutil
import subprocess
import sys
import tempfile
import time
import uuid
try:
import gi
from gi.repository import GLib
from gi.repository import Gio
gi.require_version('UMockdev', '1.0')
from gi.repository import UMockdev
except ImportError as e:
sys.stderr.write('Missing dependencies: %s\n' % str(e))
sys.exit(1)
DBUS_NAME = 'org.freedesktop.bolt'
DBUS_PATH = '/org/freedesktop/bolt'
DBUS_IFACE_PREFIX = 'org.freedesktop.bolt1.'
DBUS_IFACE_MANAGER = DBUS_IFACE_PREFIX + 'Manager'
DBUS_IFACE_DEVICE = DBUS_IFACE_PREFIX + 'Device'
DBUS_IFACE_DOMAIN = DBUS_IFACE_PREFIX + 'Domain'
SERVICE_FILE = '/usr/share/dbus-1/system-services/org.freedesktop.bolt.service'
class SysfsDev:
known_attrs = []
def __init__(self, sysname, syspath):
self.sysname = sysname
self.syspath = syspath
@property
def is_connected(self):
return self.syspath is not None
def sysattr_path(self, name):
return os.path.join(self.syspath, name)
def have_sysattr(self, name):
path = self.sysattr_path(name)
return os.path.isfile(path)
def read_sysattr(self, name):
path = self.sysattr_path(name)
try:
with open(path, 'r') as fd:
raw = fd.read()
return raw.strip()
except FileNotFoundError:
return None
def dump(self, prefix='', file=sys.stdout):
if self.sysname is None:
print('%s disconnected' % prefix, file=file)
else:
print('%s%s @ %s' % (prefix, self.sysname, self.syspath), file=file)
@staticmethod
def bridge(klass):
lst = getattr(klass, 'known_attrs', [])
def install(attr):
def getter(self):
return self.read_sysattr(attr)
prop = property(getter)
setattr(klass, attr, prop)
[install(attr) for attr in lst]
return klass
@SysfsDev.bridge
class Domain(SysfsDev):
known_attrs = ['boot_acl',
'security']
def __init__(self, index, sysname, syspath):
super(Domain, self).__init__(sysname, syspath)
self.index = index
self.parent = None
self.serial = 100
self.host = None
@property
def uid(self):
return self.host and self.host.uid
def gen_serial(self):
s = self.serial
self.serial += 1
return s
def find_device(self, name_or_id):
if self.host is None:
return None
return self.host.find_device(name_or_id)
def show(self, prefix='', file=sys.stdout):
name = self.sysname or 'domain'
uid = self.host.uid if self.host else ''
print('%s%s %s' % (prefix, name, uid), file=file)
pf = prefix + ' '
super(Domain, self).dump(prefix=pf, file=file)
security = self.security or 'unknown'
print('%ssecurity %s' % (pf, security), file=file)
@SysfsDev.bridge
class Device(SysfsDev):
known_attrs = ['authorized',
'boot',
'device',
'device_name',
'generation',
'key',
'unique_id',
'vendor',
'vendor_name']
search_fields = ('uid',
'sysname',
'device_name')
def __init__(self, uid, sysname, syspath):
super(Device, self).__init__(sysname, syspath)
self.uid = uid
self.domain = None
self.is_host = False
self.children = []
def find_device(self, identifier, fields=search_fields):
for i in [getattr(self, f) for f in fields]:
if i == identifier:
return self
for c in self.children:
d = c.find_device(identifier)
if d is not None:
return d
return None
def show(self, prefix='', file=sys.stdout):
print('%sdevice %s' % (prefix, self.uid), file=file)
pf = prefix + ' '
super(Device, self).dump(prefix=pf, file=file)
if not self.is_connected or self.is_host:
return
print('%sauthorized %s' % (pf, self.authorized), file=file)
class MockSysfs:
def __init__(self):
self.testbed = UMockdev.Testbed.new()
self.domains = {}
@property
def root(self):
return self.testbed.get_root_dir()
def _gen_domain_id(self):
for i in range(1024):
if ('domain%d' % i) not in self.domains:
return i
raise ValueError('too many domains in use')
def find_domain(self, name_or_id):
for name, dom in self.sysfs.domains.items():
if target in (name, dom.uid):
return dom
return None
def find_device(self, name_or_id):
for name, dom in self.domains.items():
d = dom.find_device(name_or_id)
if d is not None:
return d
return None
def domain_add(self, security, bootacl, iommu):
index = self._gen_domain_id()
sysname = 'domain%d' % index
props = ['security', security]
if isinstance(bootacl, int):
bootacl = ',' * (bootacl - 1)
if bootacl is not None:
props += ['boot_acl', bootacl]
if iommu is not None:
props += ['iommu_dma_protection', str(iommu) + '\n']
syspath = self.testbed.add_device('thunderbolt', sysname, None,
props,
['DEVTYPE', 'thunderbolt_domain'])
domain = Domain(index, sysname, syspath)
self.domains[sysname] = domain
return domain
def host_add(self, domain, uid, name, vendor, generation):
sysname = "%d-0" % domain.index
attributes = ['device_name', name,
'device', '0x23',
'vendor_name', vendor,
'vendor', '0x23',
'authorized', '1',
'unique_id', uid]
if generation is not None:
attributes += ['generation', str(generation)]
syspath = self.testbed.add_device('thunderbolt', sysname, domain.syspath,
attributes,
['DEVTYPE', 'thunderbolt_device'])
host = Device(uid, sysname, syspath)
host.is_host = True
domain.host = host
host.domain = domain
return host
def device_add(self, parent, uid, name, vendor, authorized, key, boot, gen):
domain = parent.domain
serial = domain.gen_serial()
sysname = "%d-%d" % (domain.index, serial)
props = ['device_name', name,
'device', '0x23',
'vendor_name', vendor,
'vendor', '0x23',
'authorized', '%d\n' % authorized,
'unique_id', uid]
if key is not None:
# The kernel always returns the key with trailing `\n`
if not key.endswith('\n'):
key += '\n'
props += ['key', key]
if boot is not None:
props += ['boot', str(boot)]
if gen is not None:
props += ['generation', str(gen)]
syspath = self.testbed.add_device('thunderbolt',
sysname, parent.syspath,
props,
['DEVTYPE', 'thunderbolt_device'])
device = Device(uid, sysname, syspath)
device.domain = domain
parent.children.append(device)
return device
def remove(self, dev):
self.testbed.uevent(dev.syspath, "remove")
self.testbed.remove_device(dev.syspath)
dev.syspath = None
dev.sysname = None
class Store:
def __init__(self, path=None):
self.path = path
self.mocked = False
if self.path is None:
self.make_mock_store()
def __del__(self):
if self.mocked:
shutil.rmtree(self.path)
def make_mock_store(self):
path = tempfile.mkdtemp()
os.makedirs(os.path.join(path, 'devices'))
os.makedirs(os.path.join(path, 'domains'))
os.makedirs(os.path.join(path, 'keys'))
self.path = path
self.mocked = True
class Daemon:
def __init__(self, store, sysfs):
self._discover_binary()
self.rundir = tempfile.mkdtemp()
self.store = store
self.sysfs = sysfs
self.log = None
self._boltd = None
def __del__(self):
if self.is_running:
self.stop()
shutil.rmtree(self.rundir)
@staticmethod
def path_from_service_file(sf):
with open(sf) as f:
for line in f:
if not line.startswith('Exec='):
continue
return line.split('=', 1)[1].strip()
return None
def _discover_binary(self):
if 'BOLT_BUILD_DIR' in os.environ:
print('Using boltd from local build')
build_dir = os.environ['BOLT_BUILD_DIR']
boltd = os.path.join(build_dir, 'boltd')
boltctl = os.path.join(build_dir, 'boltctl')
elif 'UNDER_JHBUILD' in os.environ:
print('Using boltd from JHBuild')
jhbuild_prefix = os.environ['JHBUILD_PREFIX']
boltd = os.path.join(jhbuild_prefix, 'libexec', 'boltd')
boltctl = os.path.join(jhbuild_prefix, 'bin', 'boltctl')
elif os.path.exists(os.path.abspath('build/boltd')):
build_dir = os.path.abspath('build')
print('Using boltd from %s' % build_dir)
boltd = os.path.join(build_dir, 'boltd')
boltctl = os.path.join(build_dir, 'boltctl')
else:
print('Using boltd from system installation')
boltd = Daemon.path_from_service_file(SERVICE_FILE)
boltctl = shutil.which('boltctl')
assert boltd is not None, 'failed to find daemon'
assert os.access(boltctl, os.X_OK), "could not execute @ " + boltctl
self.paths = {'daemon': boltd, 'boltctl': boltctl}
self.dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
# dbus helper methods
def _get_dbus_property(self, name, interface=DBUS_IFACE_MANAGER):
proxy = Gio.DBusProxy.new_sync(self.dbus,
Gio.DBusProxyFlags.DO_NOT_AUTO_START,
None,
DBUS_NAME,
DBUS_PATH,
'org.freedesktop.DBus.Properties',
None)
return proxy.Get('(ss)', interface, name)
def start(self, args):
timeout = 10
env = os.environ.copy()
env['G_DEBUG'] = 'fatal-criticals'
env['UMOCKDEV_DIR'] = self.sysfs.root
env['STATE_DIRECTORY'] = self.store.path
env['RUNTIME_DIRECTORY'] = self.rundir
argv = [self.paths['daemon'], '--replace']
if args.verbose:
argv += ['-v']
self._boltd = subprocess.Popen(argv,
env=env,
stdout=self.log,
stderr=subprocess.STDOUT)
timeout_count = timeout * 10
timeout_sleep = 0.1
while timeout_count > 0:
time.sleep(timeout_sleep)
timeout_count -= 1
try:
self._get_dbus_property('Version')
break
except GLib.GError:
pass
else:
timeout_time = timeout * 10 * timeout_sleep
print('daemon did not start in %d seconds' % timeout_time)
self._boltd = None
return
if self._boltd.poll() is not None:
print('daemon crashed :(')
self._boltd = None
def stop(self):
if self._boltd:
try:
self._boltd.terminate()
except OSError:
pass
self._boltd.wait()
self._boltd = None
@property
def is_running(self):
return self._boltd is not None
class Command:
class SubCommand:
def __init__(self, name, desc, parser, handler):
self.name = name
self.desc = desc
self.parser = parser
self.handler = handler
def __call__(self, obj, argv, parser):
if argv.show_help:
print(self.parser.format_help())
else:
self.handler(obj, argv, self.parser)
def __init__(self, handler):
self.handler = handler
self.name = handler.__name__
self.desc = handler.__doc__ or self.name
self.parser = argparse.ArgumentParser(prog=self.name, description=self.desc, add_help=False)
self.parser.add_argument('--help', dest='show_help', action='store_true')
self.subparsers = None
self.use_parse_known = False
def __call__(self, obj, argv):
if self.use_parse_known:
args, rest = self.parser.parse_known_args(argv)
else:
args = self.parser.parse_args(argv)
rest = None
context = {'parser': self.parser, 'unknown': rest}
if args.show_help:
print(self.parser.format_help())
elif hasattr(args, 'func'):
args.func(obj, args, context)
else:
self.handler(obj, args, context)
def subcommand(self, handler):
if self.subparsers is None:
self.subparsers = self.parser.add_subparsers()
name = handler.__name__
if not name.startswith(self.name + '_'):
raise ValueError('Invalid naming scheme')
name = name[len(self.name + '_'):]
desc = handler.__doc__ or self.name
p = self.subparsers.add_parser(name, help=desc, add_help=False)
p.add_argument('--help', dest='show_help', action='store_true')
cmd = Command.SubCommand(name, desc, p, handler)
p.set_defaults(func=cmd)
return cmd
@staticmethod
def subcommand_dispatch(obj, args):
args.func(obj, args)
def command(func):
cmd = Command(func)
return cmd
def arg(name_or_flags, *args, **kwargs):
def decorator(func):
func.parser.add_argument(name_or_flags, *args, **kwargs)
return func
return decorator
def parse_known(func):
func.use_parse_known = True
return func
def collect_args(klass):
methods = [getattr(klass, m) for m in dir(klass) if not m.startswith('__')]
commands = [m for m in methods if isinstance(m, Command)]
klass.commands = {m.name: m for m in commands}
return klass
@collect_args
class World:
def __init__(self, store, sysfs, daemon):
self.store = store
self.sysfs = sysfs
self.daemon = daemon
def handle_line(self, line):
if not line:
return True
if line == 'exit':
return False
if line.startswith('#'):
return True
argv = shlex.split(line)
idx = [i for i, arg in enumerate(argv) if arg.startswith('#')]
if len(idx):
argv = argv[:idx[0]]
cmd = self.commands.get(argv[0], None)
if cmd is None:
print('unknown command')
return True
try:
cmd(self, argv[1:])
except SystemExit:
return True
return True
def loop(self):
do_loop = True
while do_loop:
try:
line = input('> ')
except EOFError:
print('Bye.')
break
do_loop = self.handle_line(line)
@command
def help(self, args, context):
for name, cmd in self.commands.items():
print('%s - %s ' % (name, cmd.desc))
@arg('--verbose', action='store_true')
@command
def start(self, args, context):
'''start the bolt daemon'''
if self.daemon.is_running:
print('boltd already running')
else:
print('starting boltd')
self.daemon.start(args)
@command
def stop(self, args, context):
'''stops the bolt daemon'''
if not self.daemon.is_running:
print('boltd not running')
else:
print('stopping boltd')
self.daemon.stop()
@command
def status(self, args, context):
'''Show overall status'''
print('sysfs: %s' % self.sysfs.root)
print('store: %s' % self.store.path)
print('boltd: %s' % self.daemon.paths['daemon'])
print('rundir: %s' % self.daemon.rundir)
print('boltd %s' % ('running' if self.daemon.is_running else 'stopped'))
@arg('file', type=argparse.FileType('r'), default=None)
@command
def load(self, args, context):
'''Load and execute a commands from a file'''
for line in args.file:
line = line.strip()
print('@ %s' % line)
self.handle_line(line)
@command
def controller(self, args, context):
'''control controllers. hah!'''
parser = context['parser']
print(parser.format_help())
@arg('--security', type=str, default='secure')
@arg('--uuid', type=str, default=None)
@arg('--iommu', type=str, choices=[None, '0', '1'], default=None)
@arg('--bootacl', type=str, default=None)
@arg('--generation', type=int, default=3)
@arg('--vendor', type=str, default='GNOME.org')
@arg('--name', type=str, default='Laptop')
@controller.subcommand
def controller_new(self, args, context):
'''create a new domain+host combination'''
security = args.security
bootacl = args.bootacl
iommu = args.iommu
gen = args.generation
uid = args.uuid or str(uuid.uuid4())
print(uid)
domain = self.sysfs.domain_add(security, bootacl, iommu)
domain.show()
name = args.name
vendor = args.vendor
host = self.sysfs.host_add(domain, uid, name, vendor, gen)
host.show()
@arg('id', type=str, help='name or uuid')
@controller.subcommand
def controller_rm(self, args, parser):
'''removes a new domain+host combination'''
target = args.id
domain = self.sysfs.find_domain(target)
if domain is None:
print('Could not find controller')
return
if domain.host is not None:
self.sysfs.remove(domain.host)
self.sysfs.remove(domain)
@command
def device(self, args, parser):
'''control devices'''
print(parser.format_help())
@arg('--key', type=str, default=None)
@arg('--boot', type=int, choices=[None, 0, 1], default=None)
@arg('--authorized', type=int, choices=[0, 1], default=0)
@arg('--uuid', type=str, default=None)
@arg('--vendor', type=str, default='GNOME.org')
@arg('--generation', type=int, default=3)
@arg('name', type=str)
@arg('parent', type=str)
@device.subcommand
def device_new(self, args, context):
'''create a new device combination'''
parent = self.sysfs.find_device(args.parent)
if parent is None:
print('unknown parent')
return
uid = args.uuid or str(uuid.uuid4())
device = self.sysfs.device_add(parent,
uid,
args.name,
args.vendor,
args.authorized,
args.key,
args.boot,
args.generation)
device.show()
@arg('id', type=str, help='name or uuid')
@device.subcommand
def device_rm(self, args, context):
'''removes a device'''
device = self.sysfs.find_device(args.id)
if device is None:
print('unknown device')
return
print('disconnecting %s' % device.uid)
self.sysfs.remove(device)
@parse_known
@command
def boltctl(self, args, context):
'''Invoke boltctl'''
boltctl = self.daemon.paths['boltctl']
bc_args = context['unknown'] or []
argv = [boltctl] + bc_args
subprocess.call(argv)
def main():
readline.set_history_length(1000)
histfile = os.path.join(os.path.expanduser("~"), ".cache", "bolt_mock")
try:
readline.read_history_file(histfile)
except FileNotFoundError:
pass
atexit.register(readline.write_history_file, histfile)
store = Store()
sysfs = MockSysfs()
daemon = Daemon(store, sysfs)
ctrl = World(store, sysfs, daemon)
ctrl.loop()
if __name__ == '__main__':
if 'umockdev' not in os.environ.get('LD_PRELOAD', ''):
wrapped = ['umockdev-wrapper'] + sys.argv
os.execvp(wrapped[0], wrapped)
main()
bolt-0.9.2/contrib/bolt.spec.in 0000664 0000000 0000000 00000005260 14174530510 0016346 0 ustar 00root root 0000000 0000000 Name: bolt
Version: @version@
Release: 0.@reltag@%{?dist}
Summary: Thunderbolt device manager
License: LGPLv2+
URL: https://gitlab.freedesktop.org/bolt/bolt
Source0: %{url}/-/archive/%{version}/%{name}-%{version}.tar.xz
BuildRequires: gcc
BuildRequires: asciidoc
BuildRequires: meson
BuildRequires: libudev-devel
BuildRequires: pkgconfig(gio-2.0)
BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(systemd)
BuildRequires: pkgconfig(libsystemd)
BuildRequires: polkit-devel
BuildRequires: systemd
%{?systemd_requires}
# for the integration test (optional)
%if 0%{?fedora}
BuildRequires: pygobject3-devel
BuildRequires: python3-dbus
BuildRequires: python3-dbusmock
BuildRequires: umockdev-devel
%endif
%description
bolt is a system daemon to manage Thunderbolt devices via a D-BUS
API. Thunderbolt 3 introduced different security modes that require
devices to be authorized before they can be used. The D-Bus API can be
used to list devices, enroll them (authorize and store them in the
local database) and forget them again (remove previously enrolled
devices). It also emits signals if new devices are connected (or
removed). During enrollment devices can be set to be automatically
authorized as soon as they are connected. A command line tool, called
boltctl, can be used to control the daemon and perform all the above
mentioned tasks.
%package tests
Summary: Integration tests, unit tests and bolt-mock test helper
Requires: %{name}%{?_isa} = %{version}-%{release}
License: LGPLv2+
%description tests
Contains the unit tests suite, the integration tests and a bolt-mock
helper that can be used to run the boltd daemon in a mock environment
where thunderbolt domains and devices can be simulated.
%prep
%setup -q
%build
%meson -Ddb-name=@dbname@ -Dinstall-tests=true
%meson_build
%check
%meson_test
%install
%meson_install
%post
%systemd_post %{name}.service
%preun
%systemd_preun %{name}.service
%postun
%systemd_postun_with_restart %{name}.service
%files
%license COPYING
%doc README.md CHANGELOG.md BUGS.md INSTALL.md HACKING.md
%{_bindir}/boltctl
%{_libexecdir}/boltd
%{_unitdir}/%{name}.service
%{_udevrulesdir}/*-%{name}.rules
%{_datadir}/dbus-1/system.d/org.freedesktop.bolt.conf
%{_datadir}/dbus-1/interfaces/org.freedesktop.bolt.xml
%{_datadir}/polkit-1/actions/org.freedesktop.bolt.policy
%{_datadir}/polkit-1/rules.d/org.freedesktop.bolt.rules
%{_datadir}/dbus-1/system-services/org.freedesktop.bolt.service
%{_mandir}/man1/boltctl.1*
%{_mandir}/man8/boltd.8*
%ghost %dir @dbdir@
%files tests
%{_libexecdir}/installed-tests/bolt
%{_libexecdir}/installed-tests/bolt/*
%changelog
* @longdate@ Christian Kellner - @version@-@reltag@
- build from git sources.
bolt-0.9.2/contrib/cov-model.c 0000664 0000000 0000000 00000001367 14174530510 0016162 0 ustar 00root root 0000000 0000000 /* GLib types. */
typedef size_t gsize;
typedef char gchar;
typedef unsigned char guchar;
typedef int gint;
typedef unsigned long gulong;
typedef unsigned int guint32;
typedef void * gpointer;
typedef unsigned int gboolean;
void
g_assertion_message_expr (const char *domain,
const char *file,
int line,
const char *func,
const char *expr)
{
__coverity_panic__ ();
}
#define g_critical(...) __coverity_panic__ ();
/* Treat it as a memory sink to hide one-time allocation leaks. */
void
(g_once_init_leave) (volatile void *location,
gsize result)
{
__coverity_escape__ (result);
}
bolt-0.9.2/contrib/coverity.sh 0000775 0000000 0000000 00000001201 14174530510 0016317 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
set -x
GIT_DESC=$(git describe)
env
# prepare
export LC_ALL=C.UTF-8
export PYTHONPATH="/usr/share/glib-2.0"
rm -rf /build/*
# info
ls -la /build
whoami
# actual building
export CC=clang CXX=clang
meson -Dcoverity=true /build
cov-build --dir /build/cov-int ninja -C /build
pushd /build
tar -caf coverity.xz cov-int
popd
if [[ -v COVERITY_TOKEN && -v COVERITY_EMAIL ]]; then
curl --form "token=${COVERITY_TOKEN}" \
--form "email=${COVERITY_EMAIL}" \
--form "file=@/build/coverity.xz" \
--form "version=main" \
--form "description=${GIT_DESC}" \
https://scan.coverity.com/builds?project=gicmo%2Fbolt
fi
bolt-0.9.2/contrib/docker-build.sh 0000775 0000000 0000000 00000001427 14174530510 0017031 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
set -x
# prepare
export LC_ALL=C.UTF-8
export PYTHONPATH="/usr/share/glib-2.0"
rm -rf /build/*
# info
ls -la /build
whoami
# actual building
meson -Db_coverage=true -Dtests-speed=slow . /build
ninja -C /build
meson test -C /build --verbose
if [[ -x "$(command -v lcov)" ]]; then
ninja -C /build coverage
fi
if [[ -x "$(command -v scan-build)" ]]; then
ninja -C /build scan-build
if [[ -n "$(ls -A /build/meson-logs/scanbuild/)" ]]; then
echo "Scan build log found, assuming defects exist"
exit 1
fi
fi
if [[ -x "$(command -v lcov)" ]]; then
scripts/uncrustify.sh --check
fi
if [[ -x $(command -v pylint) ]]; then
pylint tests/test-integration
fi
if [[ -x $(command -v codespell) ]]; then
codespell -S .git -S build
fi
bolt-0.9.2/contrib/js/ 0000775 0000000 0000000 00000000000 14174530510 0014536 5 ustar 00root root 0000000 0000000 bolt-0.9.2/contrib/js/.jshintrc 0000664 0000000 0000000 00000013232 14174530510 0016364 0 ustar 00root root 0000000 0000000 {
"maxerr" : 50, // {int} Maximum error before stopping
// Enforcing
"bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : false, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"indent" : 4, // {int} Number of spaces to use for indentation
"latedef" : "nofunc", // true: Require variables/functions to be defined before being used
"newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` & `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : false, // true: Require all defined variables be used
"strict" : false, // true: Requires all functions run in ES5 Strict Mode
"trailing" : true, // true: Prohibit trailing whitespaces
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : false, // {int} Max number of characters per line
// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : false, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`)
"moz" : true, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements"
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : true, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : false, // true: Tolerate functions being defined in loops
"multistr" : true, // true: Tolerate multi-line strings
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function
// Environments
"browser" : false, // Web Browser (window, document, etc)
"couch" : false, // CouchDB
"devel" : false, // Development/debugging (alert, confirm, etc)
"dojo" : false, // Dojo Toolkit
"jquery" : false, // jQuery
"mootools" : false, // MooTools
"node" : false, // Node.js
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
"prototypejs" : false, // Prototype and Scriptaculous
"rhino" : false, // Rhino
"worker" : false, // Web Workers
"wsh" : false, // Windows Scripting Host
"yui" : false, // Yahoo User Interface
// Legacy
"nomen" : false, // true: Prohibit dangling `_` in variables
"onevar" : false, // true: Allow only one `var` statement per function
"passfail" : false, // true: Stop on first error
"white" : false, // true: Check against strict whitespace and indentation rules
// Custom Globals
"predef" : [ "log", "logError", "print", "printerr", "imports", "ARGV", "pkg", "_", "C_", "N_" ]
}
bolt-0.9.2/contrib/js/client.js 0000664 0000000 0000000 00000021373 14174530510 0016360 0 ustar 00root root 0000000 0000000 /*
* Copyright © 2017 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 library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors:
* Christian J. Kellner
*/
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Signals = imports.signals;
/* Keep in sync with data/org.freedesktop.bolt.xml */
const BoltClientInterface = ' \