mirror of
git://sourceware.org/git/lvm2.git
synced 2025-04-12 06:50:40 +03:00
Compare commits
No commits in common. "old-gdb-1999-05-25" and "main" have entirely different histories.
old-gdb-19
...
main
171
.gitignore
vendored
Normal file
171
.gitignore
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
*.5
|
||||
*.7
|
||||
*.8
|
||||
*.8_gen
|
||||
*.a
|
||||
*.d
|
||||
*.o
|
||||
*.orig
|
||||
*.pc
|
||||
*.pot
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*.sw*
|
||||
*.su
|
||||
*.patch
|
||||
*~
|
||||
|
||||
# gcov files:
|
||||
*.gcda
|
||||
*.gcno
|
||||
|
||||
.export.sym
|
||||
.exported_symbols_generated
|
||||
.gdb_history
|
||||
|
||||
Makefile
|
||||
make.tmpl
|
||||
|
||||
/autom4te.cache/
|
||||
/autoscan.log
|
||||
/build/
|
||||
/config.cache
|
||||
/config.log
|
||||
/config.status
|
||||
/configure.scan
|
||||
/cscope.*
|
||||
/html/
|
||||
/python/
|
||||
/reports/
|
||||
/tags
|
||||
/tmp/
|
||||
|
||||
coverity/coverity_model.xml
|
||||
|
||||
/.cache/
|
||||
/compile_commands.json
|
||||
|
||||
/doc/.ikiwiki
|
||||
/public
|
||||
|
||||
/libdm/.symver_check
|
||||
|
||||
daemons/clvmd
|
||||
daemons/dmfilemapd
|
||||
daemons/lvmetad/
|
||||
|
||||
tools/man-generator
|
||||
|
||||
test/.lib-dir-stamp
|
||||
test/.tests-stamp
|
||||
test/lib/dmsecuretest
|
||||
test/lib/lvchange
|
||||
test/lib/lvconvert
|
||||
test/lib/lvcreate
|
||||
test/lib/lvdisplay
|
||||
test/lib/lvextend
|
||||
test/lib/lvmconfig
|
||||
test/lib/lvmdiskscan
|
||||
test/lib/lvmsadc
|
||||
test/lib/lvmsar
|
||||
test/lib/lvreduce
|
||||
test/lib/lvremove
|
||||
test/lib/lvrename
|
||||
test/lib/lvresize
|
||||
test/lib/lvs
|
||||
test/lib/lvscan
|
||||
test/lib/pvchange
|
||||
test/lib/pvck
|
||||
test/lib/pvcreate
|
||||
test/lib/pvdisplay
|
||||
test/lib/pvmove
|
||||
test/lib/pvremove
|
||||
test/lib/pvresize
|
||||
test/lib/pvs
|
||||
test/lib/pvscan
|
||||
test/lib/securetest
|
||||
test/lib/vgcfgbackup
|
||||
test/lib/vgcfgrestore
|
||||
test/lib/vgchange
|
||||
test/lib/vgck
|
||||
test/lib/vgconvert
|
||||
test/lib/vgcreate
|
||||
test/lib/vgdisplay
|
||||
test/lib/vgexport
|
||||
test/lib/vgextend
|
||||
test/lib/vgimport
|
||||
test/lib/vgimportclone
|
||||
test/lib/vgmerge
|
||||
test/lib/vgmknodes
|
||||
test/lib/vgreduce
|
||||
test/lib/vgremove
|
||||
test/lib/vgrename
|
||||
test/lib/vgs
|
||||
test/lib/vgscan
|
||||
test/lib/vgsplit
|
||||
test/api/lvtest.t
|
||||
test/api/pe_start.t
|
||||
test/api/percent.t
|
||||
test/api/python_lvm_unit.py
|
||||
test/api/test
|
||||
test/api/thin_percent.t
|
||||
test/api/vglist.t
|
||||
test/api/vgtest.t
|
||||
test/lib/aux
|
||||
test/lib/cache-mq.profile
|
||||
test/lib/cache-smq.profile
|
||||
test/lib/check
|
||||
test/lib/clvmd
|
||||
test/lib/dm-version-expected
|
||||
test/lib/dmeventd
|
||||
test/lib/dmsetup
|
||||
test/lib/dmstats
|
||||
test/lib/fail
|
||||
test/lib/flavour-ndev-cluster
|
||||
test/lib/flavour-ndev-cluster-lvmpolld
|
||||
test/lib/flavour-ndev-devicesfile
|
||||
test/lib/flavour-ndev-lvmetad
|
||||
test/lib/flavour-ndev-lvmetad-lvmpolld
|
||||
test/lib/flavour-ndev-lvmpolld
|
||||
test/lib/flavour-ndev-vanilla
|
||||
test/lib/flavour-udev-cluster
|
||||
test/lib/flavour-udev-cluster-lvmpolld
|
||||
test/lib/flavour-udev-lvmetad
|
||||
test/lib/flavour-udev-lvmetad-lvmpolld
|
||||
test/lib/flavour-udev-lvmlockd-dlm
|
||||
test/lib/flavour-udev-lvmlockd-idm
|
||||
test/lib/flavour-udev-lvmlockd-sanlock
|
||||
test/lib/flavour-udev-lvmlockd-test
|
||||
test/lib/flavour-udev-lvmpolld
|
||||
test/lib/flavour-udev-vanilla
|
||||
test/lib/fsadm
|
||||
test/lib/get
|
||||
test/lib/inittest
|
||||
test/lib/invalid
|
||||
test/lib/lvm
|
||||
test/lib/lvm-wrapper
|
||||
test/lib/lvmchange
|
||||
test/lib/lvmdbusd.profile
|
||||
test/lib/lvmdevices
|
||||
test/lib/lvmetad
|
||||
test/lib/lvmlockctl
|
||||
test/lib/lvmlockd
|
||||
test/lib/lvmpolld
|
||||
test/lib/lvm_import_vdo
|
||||
test/lib/lvm_vdo_wrapper
|
||||
test/lib/not
|
||||
test/lib/paths
|
||||
test/lib/paths-common
|
||||
test/lib/runner
|
||||
test/lib/should
|
||||
test/lib/test
|
||||
test/lib/thin-performance.profile
|
||||
test/lib/utils
|
||||
test/lib/version-expected
|
||||
test/lib/vgimportdevices
|
||||
|
||||
test/unit/dmraid_t.c
|
||||
test/unit/unit-test
|
104
.gitlab-ci.yml
Normal file
104
.gitlab-ci.yml
Normal file
@ -0,0 +1,104 @@
|
||||
stages:
|
||||
- approve
|
||||
- test
|
||||
|
||||
approve1:
|
||||
stage: approve
|
||||
script:
|
||||
- echo "Approved..."
|
||||
rules:
|
||||
# TODO: Filter only safe repositories, or user in developers
|
||||
- if: $CI_PROJECT_PATH != "csonto/lvm2" && $CI_PROJECT_PATH != "lvmteam/lvm2"
|
||||
when: manual
|
||||
# TODO: for other branches than main/rhel: run pipeline only when requested:
|
||||
- if: $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH !~ "^rhel.*"
|
||||
when: manual
|
||||
- when: on_success
|
||||
allow_failure: false
|
||||
|
||||
|
||||
pages:
|
||||
image: elecnix/ikiwiki
|
||||
stage: test
|
||||
script:
|
||||
- ikiwiki --setup ikiwiki.setup --libdir themes/ikistrap/lib
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
only:
|
||||
refs:
|
||||
- main
|
||||
changes:
|
||||
- doc/**/*
|
||||
- ikiwiki.setup
|
||||
|
||||
|
||||
# TODO:
|
||||
# - check results of autoreconf and make generate - may need additional commit
|
||||
# - we need a particular setup (rawhide OR latest supported fedora?)
|
||||
# - do make rpm and publish results as artifacts - we will use packit/COPR for this eventually
|
||||
|
||||
# Run on any commits to main (master), rhel8, rhel9 branches
|
||||
test-job:
|
||||
stage: test
|
||||
parallel:
|
||||
matrix:
|
||||
- TAG: rhel8
|
||||
CONFIGURE: >
|
||||
--with-cluster=internal
|
||||
--enable-cmirrord
|
||||
- TAG: rhel9
|
||||
CONFIGURE: >
|
||||
--with-default-use-devices-file=1
|
||||
--enable-app-machineid
|
||||
--enable-editline
|
||||
--disable-readline
|
||||
artifacts:
|
||||
paths:
|
||||
- test/results/
|
||||
expire_in: 1 week
|
||||
tags:
|
||||
- ${TAG}
|
||||
timeout: 2h
|
||||
script:
|
||||
# Common options go here, diffs to the above matrix
|
||||
- >
|
||||
./configure ${CONFIGURE}
|
||||
--enable-fsadm
|
||||
--enable-write_install
|
||||
--enable-pkgconfig
|
||||
--enable-cmdlib
|
||||
--enable-dmeventd
|
||||
--enable-blkid_wiping
|
||||
--enable-udev_sync
|
||||
--with-thin=internal
|
||||
--with-cache=internal
|
||||
--enable-lvmpolld
|
||||
--enable-lvmlockd-dlm --enable-lvmlockd-dlmcontrol
|
||||
--enable-lvmlockd-sanlock
|
||||
--enable-dbus-service --enable-notify-dbus
|
||||
--enable-dmfilemapd
|
||||
--with-writecache=internal
|
||||
--with-vdo=internal --with-vdo-format=/usr/bin/vdoformat
|
||||
--with-integrity=internal
|
||||
--disable-silent-rules
|
||||
- make
|
||||
- rm -rf test/results
|
||||
- mkdir -p /dev/shm/lvm2-test
|
||||
- mount -o remount,dev /dev/shm
|
||||
# TODO: Need to distinguish failed test from failed harness
|
||||
# TODO: Also need a way to find if run is incomplete, e.g. full disk resulting in many skipped tests
|
||||
- VERBOSE=0 BATCH=1 LVM_TEST_DIR=/dev/shm/lvm2-test make check || true
|
||||
- rm -rf /dev/shm/lvm2-test
|
||||
- cut -d' ' -f2 test/results/list | sort | uniq -c
|
||||
# Filter artifacts - keep only logs from tests which are not pass
|
||||
- cd test/results && rm $(grep 'passed$' list | cut -d' ' -f1 | sed -e 's|/|_|g' -e 's|.*|\0.txt|')
|
||||
# TODO: Keep a list of known failures, and translate into regexp - or simply use python...
|
||||
- if grep failed test/results/list | grep -v '\\\(dbustest\|lvconvert-mirror\)\.sh' | sort; then false; else true; fi
|
||||
rules:
|
||||
# Filter only safe repositories, or user in developers:
|
||||
# NOTE: Already done in approve stage, may be more caution than necessary
|
||||
- if: $CI_PROJECT_PATH != "csonto/lvm2" && $CI_PROJECT_PATH != "lvmteam/lvm2"
|
||||
when: manual
|
||||
- when: on_success
|
||||
|
340
COPYING
Normal file
340
COPYING
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
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 give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
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 Program or any portion
|
||||
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
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 Program, 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 Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) 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; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, 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 executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or 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 counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program 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.
|
||||
|
||||
5. 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 Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. 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 Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program 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 Program.
|
||||
|
||||
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.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program 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.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 Program
|
||||
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 Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, 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
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), 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 Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
25
COPYING.BSD
Normal file
25
COPYING.BSD
Normal file
@ -0,0 +1,25 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2014, Red Hat, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,13 +1,14 @@
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
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 library GPL. It is
|
||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||
[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
|
||||
|
||||
@ -16,97 +17,109 @@ freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Library General Public License, applies to some
|
||||
specially designated Free Software Foundation software, and to any
|
||||
other libraries whose authors decide to use it. You can use it for
|
||||
your libraries, too.
|
||||
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, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
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
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the library, or if you modify it.
|
||||
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 a program with the library, you must provide
|
||||
complete object files to the recipients so that they can relink them
|
||||
with the library, after making changes to the library and recompiling
|
||||
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.
|
||||
|
||||
Our method of protecting your rights has two steps: (1) copyright
|
||||
the library, and (2) offer you this license which gives you legal
|
||||
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.
|
||||
|
||||
Also, for each distributor's protection, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, so that any problems introduced by others will not reflect on
|
||||
the original authors' reputations.
|
||||
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, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
transforming the program into proprietary software. To prevent this,
|
||||
we have made it clear that any patent must be licensed for everyone's
|
||||
free use or not licensed at all.
|
||||
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, which was designed for utility programs. This
|
||||
license, the GNU Library General Public License, applies to certain
|
||||
designated libraries. This license is quite different from the ordinary
|
||||
one; be sure to read it in full, and don't assume that anything in it is
|
||||
the same as in the ordinary license.
|
||||
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.
|
||||
|
||||
The reason we have a separate public license for some libraries is that
|
||||
they blur the distinction we usually make between modifying or adding to a
|
||||
program and simply using it. Linking a program with a library, without
|
||||
changing the library, is in some sense simply using the library, and is
|
||||
analogous to running a utility program or application program. However, in
|
||||
a textual and legal sense, the linked executable is a combined work, a
|
||||
derivative of the original library, and the ordinary General Public License
|
||||
treats it as such.
|
||||
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.
|
||||
|
||||
Because of this blurred distinction, using the ordinary General
|
||||
Public License for libraries did not effectively promote software
|
||||
sharing, because most developers did not use the libraries. We
|
||||
concluded that weaker conditions might promote sharing better.
|
||||
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.
|
||||
|
||||
However, unrestricted linking of non-free programs would deprive the
|
||||
users of those programs of all benefit from the free status of the
|
||||
libraries themselves. This Library General Public License is intended to
|
||||
permit developers of non-free programs to use free libraries, while
|
||||
preserving your freedom as a user of such programs to change the free
|
||||
libraries that are incorporated in them. (We have not seen how to achieve
|
||||
this as regards changes in header files, but we have achieved it as regards
|
||||
changes in the actual functions of the Library.) The hope is that this
|
||||
will lead to faster development of free libraries.
|
||||
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, while the latter only
|
||||
works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which
|
||||
contains a notice placed by the copyright holder or other authorized
|
||||
party saying it may be distributed under the terms of this Library
|
||||
General Public License (also called "this License"). Each licensee is
|
||||
addressed as "you".
|
||||
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
|
||||
@ -255,7 +268,7 @@ distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or
|
||||
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
|
||||
@ -282,23 +295,31 @@ of these things:
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Accompany the work with a written offer, valid for at
|
||||
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.
|
||||
|
||||
c) If distribution of the work is made by offering access to copy
|
||||
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.
|
||||
|
||||
d) Verify that the user has already received a copy of these
|
||||
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 source code distributed need not include anything that is normally
|
||||
distributed (in either source or binary form) with the major
|
||||
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.
|
||||
@ -347,7 +368,7 @@ Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
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
|
||||
@ -390,7 +411,7 @@ excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Library General Public License from time to time.
|
||||
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.
|
||||
|
||||
@ -436,7 +457,7 @@ DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Libraries
|
||||
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
|
||||
@ -453,18 +474,18 @@ convey the exclusion of warranty; and each file should have at least the
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
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
|
||||
Library General Public License for more details.
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
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.
|
||||
|
||||
@ -479,3 +500,5 @@ necessary. Here is a sample; alter the names:
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
31
INSTALL
Normal file
31
INSTALL
Normal file
@ -0,0 +1,31 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
1) Generate custom makefiles.
|
||||
|
||||
Run the 'configure' script from the top directory.
|
||||
|
||||
If you don't want to include the LVM1 backwards-compatibility code use:
|
||||
./configure --with-lvm1=none
|
||||
|
||||
To separate the LVM1 support into a shared library loaded by lvm.conf use:
|
||||
./configure --with-lvm1=shared
|
||||
|
||||
Use ./configure --help to see other options.
|
||||
|
||||
2) Build and install.
|
||||
|
||||
Run 'make' from the top directory to build everything you configured.
|
||||
Run 'make install' to build and install everything you configured.
|
||||
|
||||
If you only want the device-mapper libraries and tools use
|
||||
'make device-mapper' or 'make install_device-mapper'.
|
||||
|
||||
3) If using LVM2, create a configuration file.
|
||||
|
||||
The tools will work fine without a configuration file being
|
||||
present, but you ought to review the example file in doc/example.conf.
|
||||
|
||||
Please also refer to the WHATS_NEW file and the manual pages for the
|
||||
individual commands.
|
||||
|
214
Makefile.in
Normal file
214
Makefile.in
Normal file
@ -0,0 +1,214 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
|
||||
SUBDIRS = libdm conf daemons include lib libdaemon man scripts tools
|
||||
|
||||
ifeq ("@UDEV_RULES@", "yes")
|
||||
SUBDIRS += udev
|
||||
endif
|
||||
|
||||
ifeq ("@INTL@", "yes")
|
||||
SUBDIRS += po
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),clean)
|
||||
SUBDIRS += test
|
||||
endif
|
||||
# FIXME Should use intermediate Makefiles here!
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = conf include man test scripts \
|
||||
libdaemon lib tools daemons libdm \
|
||||
udev po
|
||||
tools.distclean: test.distclean
|
||||
endif
|
||||
DISTCLEAN_DIRS += lcov_reports* autom4te.cache
|
||||
DISTCLEAN_TARGETS += config.cache config.log config.status make.tmpl
|
||||
|
||||
include make.tmpl
|
||||
|
||||
include $(top_srcdir)/base/Makefile
|
||||
include $(top_srcdir)/device_mapper/Makefile
|
||||
include $(top_srcdir)/test/unit/Makefile
|
||||
|
||||
lib: include libdaemon $(BASE_TARGET) $(DEVICE_MAPPER_TARGET)
|
||||
daemons: lib libdaemon tools
|
||||
scripts: lib
|
||||
tools: lib libdaemon
|
||||
po: tools daemons
|
||||
man: tools
|
||||
all_man: tools
|
||||
test: tools daemons
|
||||
unit-test run-unit-test: test libdm
|
||||
|
||||
daemons.device-mapper: libdm.device-mapper
|
||||
tools.device-mapper: libdm.device-mapper
|
||||
device-mapper: tools.device-mapper daemons.device-mapper man.device-mapper
|
||||
device_mapper: device-mapper
|
||||
|
||||
ifeq ("@INTL@", "yes")
|
||||
lib.pofile: include.pofile
|
||||
tools.pofile: lib.pofile
|
||||
daemons.pofile: lib.pofile
|
||||
po.pofile: tools.pofile daemons.pofile
|
||||
pofile: po.pofile
|
||||
endif
|
||||
|
||||
ifneq ("$(CFLOW_CMD)", "")
|
||||
tools.cflow: libdm.cflow lib.cflow
|
||||
daemons.cflow: tools.cflow
|
||||
cflow: include.cflow
|
||||
endif
|
||||
|
||||
CSCOPE_DIRS = base daemons device_mapper include lib libdaemon scripts tools libdm test
|
||||
ifneq ("@CSCOPE_CMD@", "")
|
||||
cscope.out:
|
||||
@CSCOPE_CMD@ -b -R $(patsubst %,-s%,$(addprefix $(srcdir)/,$(CSCOPE_DIRS)))
|
||||
all: cscope.out
|
||||
endif
|
||||
DISTCLEAN_TARGETS += cscope.out
|
||||
CLEAN_DIRS += autom4te.cache
|
||||
|
||||
check check_system check_cluster check_local check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock: test
|
||||
$(MAKE) -C test $(@)
|
||||
|
||||
conf.generate man.generate: tools
|
||||
|
||||
# how to use parenthesis in makefiles
|
||||
leftparen:=(
|
||||
LVM_VER := $(firstword $(subst $(leftparen), ,$(LVM_VERSION)))
|
||||
VER := LVM2.$(LVM_VER)
|
||||
# release file name
|
||||
FILE_VER := $(VER).tgz
|
||||
CLEAN_TARGETS += $(FILE_VER)
|
||||
CLEAN_DIRS += $(rpmbuilddir)
|
||||
|
||||
dist:
|
||||
@echo "Generating $(FILE_VER)";\
|
||||
(cd $(top_srcdir); git ls-tree -r HEAD --name-only | xargs tar --transform "s,^,$(VER)/," -c) | gzip >$(FILE_VER)
|
||||
|
||||
rpm: dist
|
||||
$(RM) -r $(rpmbuilddir)/SOURCES
|
||||
$(MKDIR_P) $(rpmbuilddir)/SOURCES
|
||||
$(LN_S) -f $(abs_top_builddir)/$(FILE_VER) $(rpmbuilddir)/SOURCES
|
||||
$(LN_S) -f $(abs_top_srcdir)/spec/build.inc $(rpmbuilddir)/SOURCES
|
||||
$(LN_S) -f $(abs_top_srcdir)/spec/macros.inc $(rpmbuilddir)/SOURCES
|
||||
$(LN_S) -f $(abs_top_srcdir)/spec/packages.inc $(rpmbuilddir)/SOURCES
|
||||
DM_VER=$$(cut -d- -f1 $(top_srcdir)/VERSION_DM);\
|
||||
GIT_VER=$$(cd $(top_srcdir); git describe | cut -d- --output-delimiter=. -f2,3 || echo 0);\
|
||||
$(SED) -e "s,\(device_mapper_version\) [0-9.]*$$,\1 $$DM_VER," \
|
||||
-e "s,^\(Version:[^0-9%]*\)[0-9.]*$$,\1 $(LVM_VER)," \
|
||||
-e "s,^\(Release:[^0-9%]*\)[0-9.]\+,\1 $$GIT_VER," \
|
||||
$(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc
|
||||
V=$(V) rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec
|
||||
|
||||
generate: conf.generate man.generate
|
||||
$(MAKE) -C conf generate
|
||||
$(MAKE) -C man generate
|
||||
|
||||
all_man:
|
||||
$(MAKE) -C man all_man
|
||||
|
||||
install_system_dirs:
|
||||
$(INSTALL_DIR) $(DESTDIR)$(DEFAULT_SYS_DIR)
|
||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_SYS_DIR)/devices
|
||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_ARCHIVE_DIR)
|
||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_BACKUP_DIR)
|
||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_CACHE_DIR)
|
||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_LOCK_DIR)
|
||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_RUN_DIR)
|
||||
$(INSTALL_ROOT_DATA) /dev/null $(DESTDIR)$(DEFAULT_CACHE_DIR)/.cache
|
||||
|
||||
install_initscripts:
|
||||
$(MAKE) -C scripts install_initscripts
|
||||
|
||||
install_systemd_generators:
|
||||
$(MAKE) -C scripts install_systemd_generators
|
||||
$(MAKE) -C man install_systemd_generators
|
||||
|
||||
install_systemd_units:
|
||||
$(MAKE) -C scripts install_systemd_units
|
||||
|
||||
install_all_man:
|
||||
$(MAKE) -C man install_all_man
|
||||
|
||||
install_tmpfiles_configuration:
|
||||
$(MAKE) -C scripts install_tmpfiles_configuration
|
||||
|
||||
help:
|
||||
@echo -e "\nAvailable targets:"
|
||||
@echo " all Default target."
|
||||
@echo " all_man Build all man pages with generators."
|
||||
@echo " clean Remove all compile files."
|
||||
@echo " device-mapper Device mapper part of lvm2."
|
||||
@echo " dist Generate distributable file."
|
||||
@echo " distclean Remove all build files."
|
||||
@echo " generate Generate man pages for sources."
|
||||
@echo " help Display callable targets."
|
||||
@echo " install Install all files."
|
||||
@echo " install_all_man Install all man pages."
|
||||
@echo " install_cluster Install cmirrord."
|
||||
@echo " install_device-mapper Install device mapper files."
|
||||
@echo " install_initscripts Install initialization scripts."
|
||||
@echo " install_lvm2 Install lvm2 files."
|
||||
@echo " install_systemd_units Install systemd units."
|
||||
@echo " lcov Generate lcov output."
|
||||
@echo " lcov-dated Generate lcov with timedate suffix."
|
||||
@echo " lcov-reset Reset lcov counters"
|
||||
@echo " man Build man pages."
|
||||
@echo " print-VARIABLE Resolve make variable."
|
||||
@echo " rpm Build rpm."
|
||||
@echo " run-unit-test Run unit tests."
|
||||
@echo " tags Generate c/etags."
|
||||
|
||||
ifneq ("$(LCOV)", "")
|
||||
.PHONY: lcov-reset lcov lcov-dated
|
||||
|
||||
ifeq ($(MAKECMDGOALS),lcov-dated)
|
||||
LCOV_REPORTS_DIR := lcov_reports-$(shell date +%Y%m%d%k%M%S)
|
||||
lcov-dated: lcov
|
||||
else
|
||||
LCOV_REPORTS_DIR := lcov_reports
|
||||
endif
|
||||
|
||||
lcov-reset:
|
||||
$(LCOV) --zerocounters --directory $(top_builddir)
|
||||
|
||||
ifneq ("$(GENHTML)", "")
|
||||
lcov:
|
||||
$(RM) -rf $(LCOV_REPORTS_DIR)
|
||||
$(MKDIR_P) $(LCOV_REPORTS_DIR)
|
||||
-find . -name '*.gc[dn][ao]' ! -newer make.tmpl -delete
|
||||
-$(LCOV) --capture --directory $(top_builddir) --ignore-errors source,negative,gcov \
|
||||
--output-file $(LCOV_REPORTS_DIR)/out.info
|
||||
-test ! -s $(LCOV_REPORTS_DIR)/out.info || \
|
||||
$(GENHTML) -o $(LCOV_REPORTS_DIR) --ignore-errors source \
|
||||
$(LCOV_REPORTS_DIR)/out.info
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
ifneq ($(shell which ctags 2>/dev/null),)
|
||||
.PHONY: tags
|
||||
tags:
|
||||
test -z "$(shell find $(addprefix $(top_srcdir)/,$(CSCOPE_DIRS)) -type f -name '*.[ch]' -newer tags 2>/dev/null | head -1)" || $(RM) tags
|
||||
test -f tags || find $(addprefix $(top_srcdir)/,$(CSCOPE_DIRS)) -maxdepth 5 -type f -name '*.[ch]' -exec ctags -a '{}' +
|
||||
|
||||
CLEAN_TARGETS += tags
|
||||
endif
|
60
README
Normal file
60
README
Normal file
@ -0,0 +1,60 @@
|
||||
This tree contains the LVM2 and device-mapper tools and libraries.
|
||||
|
||||
For more information about LVM2 read the changelog in the WHATS_NEW file.
|
||||
Installation instructions are in INSTALL.
|
||||
|
||||
There is no warranty - see COPYING and COPYING.LIB.
|
||||
|
||||
Tarballs are available from:
|
||||
ftp://sourceware.org/pub/lvm2/
|
||||
https://github.com/lvmteam/lvm2/releases
|
||||
|
||||
The source code is stored in git:
|
||||
https://gitlab.com/lvmteam/lvm2
|
||||
Clone:
|
||||
git clone git@gitlab.com:lvmteam/lvm2.git
|
||||
Anonymous access:
|
||||
git clone https://gitlab.com/lvmteam/lvm2.git
|
||||
Mirrored to:
|
||||
* https://github.com/lvmteam/lvm2
|
||||
git clone https://github.com/lvmteam/lvm2.git
|
||||
git clone git@github.com:lvmteam/lvm2.git
|
||||
* https://sourceware.org/git/?p=lvm2.git
|
||||
git clone https://sourceware.org/git/lvm2.git
|
||||
git clone git://sourceware.org/git/lvm2.git
|
||||
|
||||
Mailing list for general discussion related to LVM2:
|
||||
linux-lvm@lists.linux.dev
|
||||
Subscribe via email to: linux-lvm+subscribe@lists.linux.dev
|
||||
Archive https://lore.kernel.org/linux-lvm/
|
||||
Older archive https://listman.redhat.com/archives/linux-lvm/
|
||||
|
||||
Mailing lists for LVM2 development, patches and commits:
|
||||
lvm-devel@lists.linux.dev
|
||||
Subscribe via email to: lvm-devel+subscribe@lists.linux.dev
|
||||
Archive https://lore.kernel.org/lvm-devel/
|
||||
Older archive https://listman.redhat.com/archives/lvm-devel/
|
||||
|
||||
lvm2-commits@lists.fedorahosted.org (Read-only archive of commits)
|
||||
Subscribe from https://fedorahosted.org/mailman/listinfo/lvm2-commits
|
||||
|
||||
Mailing list for device-mapper development, including kernel patches
|
||||
and multipath-tools:
|
||||
dm-devel@redhat.com
|
||||
Subscribe from https://www.redhat.com/mailman/listinfo/dm-devel
|
||||
|
||||
Website:
|
||||
https://sourceware.org/lvm2/
|
||||
|
||||
Report upstream bugs at:
|
||||
https://bugzilla.redhat.com/enter_bug.cgi?product=LVM%20and%20device-mapper
|
||||
or open issues at:
|
||||
https://gitlab.com/groups/lvmteam/-/issues
|
||||
https://github.com/lvmteam/lvm2/issues
|
||||
|
||||
The source code repository used until 7th June 2012 is accessible using CVS:
|
||||
|
||||
cvs -d :pserver:cvs@sourceware.org:/cvs/lvm2 login cvs
|
||||
cvs -d :pserver:cvs@sourceware.org:/cvs/lvm2 checkout LVM2
|
||||
|
||||
The password is cvs.
|
67
TESTING
Normal file
67
TESTING
Normal file
@ -0,0 +1,67 @@
|
||||
LVM2 Test Suite
|
||||
===============
|
||||
|
||||
The codebase contains many tests in the test subdirectory.
|
||||
|
||||
Before running tests
|
||||
--------------------
|
||||
|
||||
Keep in mind the testsuite MUST run under root user.
|
||||
|
||||
It is recommended not to use LVM on the test machine, especially when running
|
||||
tests with udev (`make check_system`.)
|
||||
|
||||
You MUST disable (or mask) any LVM daemons:
|
||||
|
||||
- lvmetad
|
||||
- dmeventd
|
||||
- lvmpolld
|
||||
- lvmdbusd
|
||||
- lvmlockd
|
||||
- clvmd
|
||||
- cmirrord
|
||||
|
||||
Some lvm.conf options should be set:
|
||||
|
||||
- global/event_activation = 0
|
||||
- activation/monitoring = 0
|
||||
|
||||
For running cluster tests, we are using singlenode locking. Pass
|
||||
`--with-clvmd=singlenode` to configure.
|
||||
|
||||
NOTE: This is useful only for testing, and should not be used in production
|
||||
code.
|
||||
|
||||
To run D-Bus daemon tests, existing D-Bus session is required.
|
||||
|
||||
Running tests
|
||||
-------------
|
||||
|
||||
As root run:
|
||||
|
||||
make check
|
||||
|
||||
To run only tests matching a string:
|
||||
|
||||
make check T=test
|
||||
|
||||
To skip tests matching a string:
|
||||
|
||||
make check S=test
|
||||
|
||||
There are other targets and many environment variables can be used to tweak the
|
||||
testsuite - for full list and description run `make -C test help`.
|
||||
|
||||
Installing testsuite
|
||||
--------------------
|
||||
|
||||
It is possible to install and run a testsuite against installed LVM. Run the
|
||||
following:
|
||||
|
||||
make -C test install
|
||||
|
||||
Then lvm2-testsuite binary can be executed to test installed binaries.
|
||||
|
||||
See `lvm2-testsuite --help` for options. The same environment variables can be
|
||||
used as with `make check`.
|
||||
|
1
VERSION_DM
Normal file
1
VERSION_DM
Normal file
@ -0,0 +1 @@
|
||||
1.02.206-git (2025-02-27)
|
1591
WHATS_NEW_DM
Normal file
1591
WHATS_NEW_DM
Normal file
File diff suppressed because it is too large
Load Diff
252
acinclude.m4
Normal file
252
acinclude.m4
Normal file
@ -0,0 +1,252 @@
|
||||
dnl AC_GCC_VERSION
|
||||
dnl check for compiler version
|
||||
dnl sets COMPILER_VERSION and GCC_VERSION
|
||||
|
||||
AC_DEFUN([AC_CC_VERSION],
|
||||
[
|
||||
AC_MSG_CHECKING([C compiler version])
|
||||
COMPILER_VERSION=`$CC -v 2>&1 | grep version`
|
||||
case "$COMPILER_VERSION" in
|
||||
*gcc*)
|
||||
dnl Ok, how to turn $3 into the real $3
|
||||
GCC_VERSION=`echo $COMPILER_VERSION | \
|
||||
sed -e 's/[[^ ]]*\ [[^ ]]*\ \([[^ ]]*\)\ .*/\1/'` ;;
|
||||
*) GCC_VERSION=unknown ;;
|
||||
esac
|
||||
AC_MSG_RESULT($GCC_VERSION)
|
||||
])
|
||||
|
||||
dnl AC_TRY_CCFLAG([CCFLAG], [VAR], [ACTION-IF-WORKS], [ACTION-IF-FAILS])
|
||||
dnl check if $CC supports a given flag
|
||||
|
||||
AC_DEFUN([AC_TRY_CCFLAG],
|
||||
[
|
||||
AC_REQUIRE([AC_PROG_CC])
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS=$1
|
||||
AC_CACHE_CHECK([whether $CC accepts $1 flag], [ac_cv_flag_$2],
|
||||
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
|
||||
[AS_VAR_SET([ac_cv_flag_$2], [yes])],
|
||||
[AS_VAR_SET([ac_cv_flag_$2], [no])])])
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
$2=AS_VAR_GET([ac_cv_flag_$2])
|
||||
if test "$2" = yes; then
|
||||
ifelse([$3], [], [:], [$3])
|
||||
else
|
||||
ifelse([$4], [], [:], [$4])
|
||||
fi
|
||||
])
|
||||
|
||||
dnl AC_IF_YES([TEST-FOR-YES], [ACTION-IF-TRUE], [ACTION-IF-FALSE])
|
||||
dnl AS_IF() abstraction, checks shell variable for 'yes'
|
||||
AC_DEFUN([AC_IF_YES], [AS_IF([test $$1 = yes], [$2], [$3])])
|
||||
|
||||
dnl AC_TRY_LDFLAGS([LDFLAGS], [VAR], [ACTION-IF-WORKS], [ACTION-IF-FAILS])
|
||||
dnl check if $CC supports given ld flags
|
||||
|
||||
AC_DEFUN([AC_TRY_LDFLAGS],
|
||||
[
|
||||
AC_REQUIRE([AC_PROG_CC])
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS=$1
|
||||
AC_CACHE_CHECK([whether $CC accepts $1 ld flags], [ac_cv_flag_$2],
|
||||
[AC_LINK_IFELSE([AC_LANG_PROGRAM()],
|
||||
[AS_VAR_SET([ac_cv_flag_$2], [yes])],
|
||||
[AS_VAR_SET([ac_cv_flag_$2], [no])])])
|
||||
LDFLAGS=$ac_save_LDFLAGS
|
||||
$2=AS_VAR_GET([ac_cv_flag_$2])
|
||||
if test "$2" = yes; then
|
||||
ifelse([$3], [], [:], [$3])
|
||||
else
|
||||
ifelse([$4], [], [:], [$4])
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
||||
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl -------------------------------------------
|
||||
dnl Since: 0.28
|
||||
dnl
|
||||
dnl Retrieves the value of the pkg-config variable for the given module.
|
||||
AC_DEFUN([PKG_CHECK_VAR],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
|
||||
|
||||
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
|
||||
AS_VAR_COPY([$1], [pkg_cv_][$1])
|
||||
|
||||
AS_VAR_IF([$1], [""], [$5], [$4])dnl
|
||||
])dnl PKG_CHECK_VAR
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_gcc_builtin.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_GCC_BUILTIN(BUILTIN)
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro checks if the compiler supports one of GCC's built-in
|
||||
# functions; many other compilers also provide those same built-ins.
|
||||
#
|
||||
# The BUILTIN parameter is the name of the built-in function.
|
||||
#
|
||||
# If BUILTIN is supported define HAVE_<BUILTIN>. Keep in mind that since
|
||||
# builtins usually start with two underscores they will be copied over
|
||||
# into the HAVE_<BUILTIN> definition (e.g. HAVE___BUILTIN_EXPECT for
|
||||
# __builtin_expect()).
|
||||
#
|
||||
# The macro caches its result in the ax_cv_have_<BUILTIN> variable (e.g.
|
||||
# ax_cv_have___builtin_expect).
|
||||
#
|
||||
# The macro currently supports the following built-in functions:
|
||||
#
|
||||
# __builtin_assume_aligned
|
||||
# __builtin_bswap16
|
||||
# __builtin_bswap32
|
||||
# __builtin_bswap64
|
||||
# __builtin_choose_expr
|
||||
# __builtin___clear_cache
|
||||
# __builtin_clrsb
|
||||
# __builtin_clrsbl
|
||||
# __builtin_clrsbll
|
||||
# __builtin_clz
|
||||
# __builtin_clzl
|
||||
# __builtin_clzll
|
||||
# __builtin_complex
|
||||
# __builtin_constant_p
|
||||
# __builtin_ctz
|
||||
# __builtin_ctzl
|
||||
# __builtin_ctzll
|
||||
# __builtin_expect
|
||||
# __builtin_ffs
|
||||
# __builtin_ffsl
|
||||
# __builtin_ffsll
|
||||
# __builtin_fpclassify
|
||||
# __builtin_huge_val
|
||||
# __builtin_huge_valf
|
||||
# __builtin_huge_vall
|
||||
# __builtin_inf
|
||||
# __builtin_infd128
|
||||
# __builtin_infd32
|
||||
# __builtin_infd64
|
||||
# __builtin_inff
|
||||
# __builtin_infl
|
||||
# __builtin_isinf_sign
|
||||
# __builtin_nan
|
||||
# __builtin_nand128
|
||||
# __builtin_nand32
|
||||
# __builtin_nand64
|
||||
# __builtin_nanf
|
||||
# __builtin_nanl
|
||||
# __builtin_nans
|
||||
# __builtin_nansf
|
||||
# __builtin_nansl
|
||||
# __builtin_object_size
|
||||
# __builtin_parity
|
||||
# __builtin_parityl
|
||||
# __builtin_parityll
|
||||
# __builtin_popcount
|
||||
# __builtin_popcountl
|
||||
# __builtin_popcountll
|
||||
# __builtin_powi
|
||||
# __builtin_powif
|
||||
# __builtin_powil
|
||||
# __builtin_prefetch
|
||||
# __builtin_trap
|
||||
# __builtin_types_compatible_p
|
||||
# __builtin_unreachable
|
||||
#
|
||||
# Unsuppored built-ins will be tested with an empty parameter set and the
|
||||
# result of the check might be wrong or meaningless so use with care.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2013 Gabriele Svelto <gabriele.svelto@gmail.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
serial 3
|
||||
|
||||
AC_DEFUN([AX_GCC_BUILTIN], [
|
||||
AS_VAR_PUSHDEF([ac_var], [ax_cv_have_$1])
|
||||
|
||||
AC_CACHE_CHECK([for $1], [ac_var], [
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [
|
||||
m4_case([$1],
|
||||
[__builtin_assume_aligned], [$1("", 0)],
|
||||
[__builtin_bswap16], [$1(0)],
|
||||
[__builtin_bswap32], [$1(0)],
|
||||
[__builtin_bswap64], [$1(0)],
|
||||
[__builtin_choose_expr], [$1(0, 0, 0)],
|
||||
[__builtin___clear_cache], [$1("", "")],
|
||||
[__builtin_clrsb], [$1(0)],
|
||||
[__builtin_clrsbl], [$1(0)],
|
||||
[__builtin_clrsbll], [$1(0)],
|
||||
[__builtin_clz], [$1(0)],
|
||||
[__builtin_clzl], [$1(0)],
|
||||
[__builtin_clzll], [$1(0)],
|
||||
[__builtin_complex], [$1(0.0, 0.0)],
|
||||
[__builtin_constant_p], [$1(0)],
|
||||
[__builtin_ctz], [$1(0)],
|
||||
[__builtin_ctzl], [$1(0)],
|
||||
[__builtin_ctzll], [$1(0)],
|
||||
[__builtin_expect], [$1(0, 0)],
|
||||
[__builtin_ffs], [$1(0)],
|
||||
[__builtin_ffsl], [$1(0)],
|
||||
[__builtin_ffsll], [$1(0)],
|
||||
[__builtin_fpclassify], [$1(0, 1, 2, 3, 4, 0.0)],
|
||||
[__builtin_huge_val], [$1()],
|
||||
[__builtin_huge_valf], [$1()],
|
||||
[__builtin_huge_vall], [$1()],
|
||||
[__builtin_inf], [$1()],
|
||||
[__builtin_infd128], [$1()],
|
||||
[__builtin_infd32], [$1()],
|
||||
[__builtin_infd64], [$1()],
|
||||
[__builtin_inff], [$1()],
|
||||
[__builtin_infl], [$1()],
|
||||
[__builtin_isinf_sign], [$1(0.0)],
|
||||
[__builtin_nan], [$1("")],
|
||||
[__builtin_nand128], [$1("")],
|
||||
[__builtin_nand32], [$1("")],
|
||||
[__builtin_nand64], [$1("")],
|
||||
[__builtin_nanf], [$1("")],
|
||||
[__builtin_nanl], [$1("")],
|
||||
[__builtin_nans], [$1("")],
|
||||
[__builtin_nansf], [$1("")],
|
||||
[__builtin_nansl], [$1("")],
|
||||
[__builtin_object_size], [$1("", 0)],
|
||||
[__builtin_parity], [$1(0)],
|
||||
[__builtin_parityl], [$1(0)],
|
||||
[__builtin_parityll], [$1(0)],
|
||||
[__builtin_popcount], [$1(0)],
|
||||
[__builtin_popcountl], [$1(0)],
|
||||
[__builtin_popcountll], [$1(0)],
|
||||
[__builtin_powi], [$1(0, 0)],
|
||||
[__builtin_powif], [$1(0, 0)],
|
||||
[__builtin_powil], [$1(0, 0)],
|
||||
[__builtin_prefetch], [$1("")],
|
||||
[__builtin_trap], [$1()],
|
||||
[__builtin_types_compatible_p], [$1(int, int)],
|
||||
[__builtin_unreachable], [$1()],
|
||||
[m4_warn([syntax], [Unsupported built-in $1, the test may fail])
|
||||
$1()]
|
||||
)
|
||||
])],
|
||||
[AS_VAR_SET([ac_var], [yes])],
|
||||
[AS_VAR_SET([ac_var], [no])])
|
||||
])
|
||||
|
||||
AS_IF([test yes = AS_VAR_GET([ac_var])],
|
||||
[AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1,
|
||||
[Define to 1 if the system has the `$1' built-in function])], [])
|
||||
|
||||
AS_VAR_POPDEF([ac_var])
|
||||
])
|
812
aclocal.m4
vendored
Normal file
812
aclocal.m4
vendored
Normal file
@ -0,0 +1,812 @@
|
||||
# generated automatically by aclocal 1.17 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996-2024 Free Software Foundation, Inc.
|
||||
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
|
||||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_python_module.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PYTHON_MODULE(modname[, fatal, python])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Checks for Python module.
|
||||
#
|
||||
# If fatal is non-empty then absence of a module will trigger an error.
|
||||
# The third parameter can either be "python" for Python 2 or "python3" for
|
||||
# Python 3; defaults to Python 3.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Andrew Collier
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 9
|
||||
|
||||
AU_ALIAS([AC_PYTHON_MODULE], [AX_PYTHON_MODULE])
|
||||
AC_DEFUN([AX_PYTHON_MODULE],[
|
||||
if test -z $PYTHON;
|
||||
then
|
||||
if test -z "$3";
|
||||
then
|
||||
PYTHON="python3"
|
||||
else
|
||||
PYTHON="$3"
|
||||
fi
|
||||
fi
|
||||
PYTHON_NAME=`basename $PYTHON`
|
||||
AC_MSG_CHECKING($PYTHON_NAME module: $1)
|
||||
$PYTHON -c "import $1" 2>/dev/null
|
||||
if test $? -eq 0;
|
||||
then
|
||||
AC_MSG_RESULT(yes)
|
||||
eval AS_TR_CPP(HAVE_PYMOD_$1)=yes
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
eval AS_TR_CPP(HAVE_PYMOD_$1)=no
|
||||
#
|
||||
if test -n "$2"
|
||||
then
|
||||
AC_MSG_ERROR(failed to find required module $1)
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
# pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*-
|
||||
# serial 12 (pkg-config-0.29.2)
|
||||
|
||||
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
|
||||
dnl
|
||||
dnl This program is free software; you can redistribute it and/or modify
|
||||
dnl it under the terms of the GNU General Public License as published by
|
||||
dnl the Free Software Foundation; either version 2 of the License, or
|
||||
dnl (at your option) any later version.
|
||||
dnl
|
||||
dnl This program is distributed in the hope that it will be useful, but
|
||||
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
dnl General Public License for more details.
|
||||
dnl
|
||||
dnl You should have received a copy of the GNU General Public License
|
||||
dnl along with this program; if not, write to the Free Software
|
||||
dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
dnl 02111-1307, USA.
|
||||
dnl
|
||||
dnl As a special exception to the GNU General Public License, if you
|
||||
dnl distribute this file as part of a program that contains a
|
||||
dnl configuration script generated by Autoconf, you may include it under
|
||||
dnl the same distribution terms that you use for the rest of that
|
||||
dnl program.
|
||||
|
||||
dnl PKG_PREREQ(MIN-VERSION)
|
||||
dnl -----------------------
|
||||
dnl Since: 0.29
|
||||
dnl
|
||||
dnl Verify that the version of the pkg-config macros are at least
|
||||
dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
|
||||
dnl installed version of pkg-config, this checks the developer's version
|
||||
dnl of pkg.m4 when generating configure.
|
||||
dnl
|
||||
dnl To ensure that this macro is defined, also add:
|
||||
dnl m4_ifndef([PKG_PREREQ],
|
||||
dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
|
||||
dnl
|
||||
dnl See the "Since" comment for each macro you use to see what version
|
||||
dnl of the macros you require.
|
||||
m4_defun([PKG_PREREQ],
|
||||
[m4_define([PKG_MACROS_VERSION], [0.29.2])
|
||||
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
|
||||
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
|
||||
])dnl PKG_PREREQ
|
||||
|
||||
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION], [ACTION-IF-NOT-FOUND])
|
||||
dnl ---------------------------------------------------------
|
||||
dnl Since: 0.16
|
||||
dnl
|
||||
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
|
||||
dnl first found in the path. Checks that the version of pkg-config found
|
||||
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
|
||||
dnl used since that's the first version where most current features of
|
||||
dnl pkg-config existed.
|
||||
dnl
|
||||
dnl If pkg-config is not found or older than specified, it will result
|
||||
dnl in an empty PKG_CONFIG variable. To avoid widespread issues with
|
||||
dnl scripts not checking it, ACTION-IF-NOT-FOUND defaults to aborting.
|
||||
dnl You can specify [PKG_CONFIG=false] as an action instead, which would
|
||||
dnl result in pkg-config tests failing, but no bogus error messages.
|
||||
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
||||
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
|
||||
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
|
||||
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
|
||||
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
|
||||
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
|
||||
|
||||
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
|
||||
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
|
||||
fi
|
||||
if test -n "$PKG_CONFIG"; then
|
||||
_pkg_min_version=m4_default([$1], [0.9.0])
|
||||
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
PKG_CONFIG=""
|
||||
fi
|
||||
fi
|
||||
if test -z "$PKG_CONFIG"; then
|
||||
m4_default([$2], [AC_MSG_ERROR([pkg-config not found])])
|
||||
fi[]dnl
|
||||
])dnl PKG_PROG_PKG_CONFIG
|
||||
|
||||
dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl -------------------------------------------------------------------
|
||||
dnl Since: 0.18
|
||||
dnl
|
||||
dnl Check to see whether a particular set of modules exists. Similar to
|
||||
dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
dnl
|
||||
dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
dnl only at the first occurrence in configure.ac, so if the first place
|
||||
dnl it's called might be skipped (such as if it is within an "if", you
|
||||
dnl have to call PKG_CHECK_EXISTS manually
|
||||
AC_DEFUN([PKG_CHECK_EXISTS],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
|
||||
m4_default([$2], [:])
|
||||
m4_ifvaln([$3], [else
|
||||
$3])dnl
|
||||
fi])
|
||||
|
||||
dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
dnl ---------------------------------------------
|
||||
dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
|
||||
dnl pkg_failed based on the result.
|
||||
m4_define([_PKG_CONFIG],
|
||||
[if test -n "$$1"; then
|
||||
pkg_cv_[]$1="$$1"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
PKG_CHECK_EXISTS([$3],
|
||||
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes ],
|
||||
[pkg_failed=yes])
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi[]dnl
|
||||
])dnl _PKG_CONFIG
|
||||
|
||||
dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||
dnl ---------------------------
|
||||
dnl Internal check to see if pkg-config supports short errors.
|
||||
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi[]dnl
|
||||
])dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||
|
||||
|
||||
dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
dnl [ACTION-IF-NOT-FOUND])
|
||||
dnl --------------------------------------------------------------
|
||||
dnl Since: 0.4.0
|
||||
dnl
|
||||
dnl Note that if there is a possibility the first call to
|
||||
dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||
dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||
AC_DEFUN([PKG_CHECK_MODULES],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
|
||||
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
|
||||
|
||||
pkg_failed=no
|
||||
AC_MSG_CHECKING([for $2])
|
||||
|
||||
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
|
||||
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
|
||||
|
||||
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
|
||||
and $1[]_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.])
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
AC_MSG_RESULT([no])
|
||||
_PKG_SHORT_ERRORS_SUPPORTED
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
|
||||
else
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
|
||||
|
||||
m4_default([$4], [AC_MSG_ERROR(
|
||||
[Package requirements ($2) were not met:
|
||||
|
||||
$$1_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
_PKG_TEXT])[]dnl
|
||||
])
|
||||
elif test $pkg_failed = untried; then
|
||||
AC_MSG_RESULT([no])
|
||||
m4_default([$4], [AC_MSG_FAILURE(
|
||||
[The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
_PKG_TEXT
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
|
||||
])
|
||||
else
|
||||
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
|
||||
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
|
||||
AC_MSG_RESULT([yes])
|
||||
$3
|
||||
fi[]dnl
|
||||
])dnl PKG_CHECK_MODULES
|
||||
|
||||
|
||||
dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
dnl [ACTION-IF-NOT-FOUND])
|
||||
dnl ---------------------------------------------------------------------
|
||||
dnl Since: 0.29
|
||||
dnl
|
||||
dnl Checks for existence of MODULES and gathers its build flags with
|
||||
dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
|
||||
dnl and VARIABLE-PREFIX_LIBS from --libs.
|
||||
dnl
|
||||
dnl Note that if there is a possibility the first call to
|
||||
dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
|
||||
dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
|
||||
dnl configure.ac.
|
||||
AC_DEFUN([PKG_CHECK_MODULES_STATIC],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
_save_PKG_CONFIG=$PKG_CONFIG
|
||||
PKG_CONFIG="$PKG_CONFIG --static"
|
||||
PKG_CHECK_MODULES($@)
|
||||
PKG_CONFIG=$_save_PKG_CONFIG[]dnl
|
||||
])dnl PKG_CHECK_MODULES_STATIC
|
||||
|
||||
|
||||
dnl PKG_INSTALLDIR([DIRECTORY])
|
||||
dnl -------------------------
|
||||
dnl Since: 0.27
|
||||
dnl
|
||||
dnl Substitutes the variable pkgconfigdir as the location where a module
|
||||
dnl should install pkg-config .pc files. By default the directory is
|
||||
dnl $libdir/pkgconfig, but the default can be changed by passing
|
||||
dnl DIRECTORY. The user can override through the --with-pkgconfigdir
|
||||
dnl parameter.
|
||||
AC_DEFUN([PKG_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
[pkg-config installation directory @<:@]pkg_default[@:>@])
|
||||
AC_ARG_WITH([pkgconfigdir],
|
||||
[AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
|
||||
[with_pkgconfigdir=]pkg_default)
|
||||
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
])dnl PKG_INSTALLDIR
|
||||
|
||||
|
||||
dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
|
||||
dnl --------------------------------
|
||||
dnl Since: 0.27
|
||||
dnl
|
||||
dnl Substitutes the variable noarch_pkgconfigdir as the location where a
|
||||
dnl module should install arch-independent pkg-config .pc files. By
|
||||
dnl default the directory is $datadir/pkgconfig, but the default can be
|
||||
dnl changed by passing DIRECTORY. The user can override through the
|
||||
dnl --with-noarch-pkgconfigdir parameter.
|
||||
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
[pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
|
||||
AC_ARG_WITH([noarch-pkgconfigdir],
|
||||
[AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
|
||||
[with_noarch_pkgconfigdir=]pkg_default)
|
||||
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
])dnl PKG_NOARCH_INSTALLDIR
|
||||
|
||||
|
||||
dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
||||
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl -------------------------------------------
|
||||
dnl Since: 0.28
|
||||
dnl
|
||||
dnl Retrieves the value of the pkg-config variable for the given module.
|
||||
AC_DEFUN([PKG_CHECK_VAR],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
|
||||
|
||||
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
|
||||
AS_VAR_COPY([$1], [pkg_cv_][$1])
|
||||
|
||||
AS_VAR_IF([$1], [""], [$5], [$4])dnl
|
||||
])dnl PKG_CHECK_VAR
|
||||
|
||||
dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES,
|
||||
dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND],
|
||||
dnl [DESCRIPTION], [DEFAULT])
|
||||
dnl ------------------------------------------
|
||||
dnl
|
||||
dnl Prepare a "--with-" configure option using the lowercase
|
||||
dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and
|
||||
dnl PKG_CHECK_MODULES in a single macro.
|
||||
AC_DEFUN([PKG_WITH_MODULES],
|
||||
[
|
||||
m4_pushdef([with_arg], m4_tolower([$1]))
|
||||
|
||||
m4_pushdef([description],
|
||||
[m4_default([$5], [build with ]with_arg[ support])])
|
||||
|
||||
m4_pushdef([def_arg], [m4_default([$6], [auto])])
|
||||
m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes])
|
||||
m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no])
|
||||
|
||||
m4_case(def_arg,
|
||||
[yes],[m4_pushdef([with_without], [--without-]with_arg)],
|
||||
[m4_pushdef([with_without],[--with-]with_arg)])
|
||||
|
||||
AC_ARG_WITH(with_arg,
|
||||
AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),,
|
||||
[AS_TR_SH([with_]with_arg)=def_arg])
|
||||
|
||||
AS_CASE([$AS_TR_SH([with_]with_arg)],
|
||||
[yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)],
|
||||
[auto],[PKG_CHECK_MODULES([$1],[$2],
|
||||
[m4_n([def_action_if_found]) $3],
|
||||
[m4_n([def_action_if_not_found]) $4])])
|
||||
|
||||
m4_popdef([with_arg])
|
||||
m4_popdef([description])
|
||||
m4_popdef([def_arg])
|
||||
|
||||
])dnl PKG_WITH_MODULES
|
||||
|
||||
dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
|
||||
dnl [DESCRIPTION], [DEFAULT])
|
||||
dnl -----------------------------------------------
|
||||
dnl
|
||||
dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES
|
||||
dnl check._[VARIABLE-PREFIX] is exported as make variable.
|
||||
AC_DEFUN([PKG_HAVE_WITH_MODULES],
|
||||
[
|
||||
PKG_WITH_MODULES([$1],[$2],,,[$3],[$4])
|
||||
|
||||
AM_CONDITIONAL([HAVE_][$1],
|
||||
[test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"])
|
||||
])dnl PKG_HAVE_WITH_MODULES
|
||||
|
||||
dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
|
||||
dnl [DESCRIPTION], [DEFAULT])
|
||||
dnl ------------------------------------------------------
|
||||
dnl
|
||||
dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after
|
||||
dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make
|
||||
dnl and preprocessor variable.
|
||||
AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES],
|
||||
[
|
||||
PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4])
|
||||
|
||||
AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
|
||||
[AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])])
|
||||
])dnl PKG_HAVE_DEFINE_WITH_MODULES
|
||||
|
||||
# Copyright (C) 1999-2024 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
|
||||
# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
# ---------------------------------------------------------------------------
|
||||
# Adds support for distributing Python modules and packages. To
|
||||
# install modules, copy them to $(pythondir), using the python_PYTHON
|
||||
# automake variable. To install a package with the same name as the
|
||||
# automake package, install to $(pkgpythondir), or use the
|
||||
# pkgpython_PYTHON automake variable.
|
||||
#
|
||||
# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as
|
||||
# locations to install python extension modules (shared libraries).
|
||||
# Another macro is required to find the appropriate flags to compile
|
||||
# extension modules.
|
||||
#
|
||||
# If your package is configured with a different prefix to python,
|
||||
# users will have to add the install directory to the PYTHONPATH
|
||||
# environment variable, or create a .pth file (see the python
|
||||
# documentation for details).
|
||||
#
|
||||
# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will
|
||||
# cause an error if the version of python installed on the system
|
||||
# doesn't meet the requirement. MINIMUM-VERSION should consist of
|
||||
# numbers and dots only.
|
||||
AC_DEFUN([AM_PATH_PYTHON],
|
||||
[
|
||||
dnl Find a Python interpreter. Python versions prior to 2.0 are not
|
||||
dnl supported. (2.0 was released on October 16, 2000).
|
||||
m4_define_default([_AM_PYTHON_INTERPRETER_LIST],
|
||||
[python python3 dnl
|
||||
python3.20 python3.19 python3.18 python3.17 python3.16 dnl
|
||||
python3.15 python3.14 python3.13 python3.12 python3.11 python3.10 dnl
|
||||
python3.9 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 dnl
|
||||
python3.2 python3.1 python3.0 dnl
|
||||
python2 dnl
|
||||
python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 dnl
|
||||
python2.0])
|
||||
|
||||
AC_ARG_VAR([PYTHON], [the Python interpreter])
|
||||
|
||||
m4_if([$1],[],[
|
||||
dnl No version check is needed.
|
||||
# Find any Python interpreter.
|
||||
if test -z "$PYTHON"; then
|
||||
AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :)
|
||||
fi
|
||||
am_display_PYTHON=python
|
||||
], [
|
||||
dnl A version check is needed.
|
||||
if test -n "$PYTHON"; then
|
||||
# If the user set $PYTHON, use it and don't search something else.
|
||||
AC_MSG_CHECKING([whether $PYTHON version is >= $1])
|
||||
AM_PYTHON_CHECK_VERSION([$PYTHON], [$1],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([Python interpreter is too old])])
|
||||
am_display_PYTHON=$PYTHON
|
||||
else
|
||||
# Otherwise, try each interpreter until we find one that satisfies
|
||||
# VERSION.
|
||||
AC_CACHE_CHECK([for a Python interpreter with version >= $1],
|
||||
[am_cv_pathless_PYTHON],[
|
||||
for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do
|
||||
test "$am_cv_pathless_PYTHON" = none && break
|
||||
AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break])
|
||||
done])
|
||||
# Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
|
||||
if test "$am_cv_pathless_PYTHON" = none; then
|
||||
PYTHON=:
|
||||
else
|
||||
AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON])
|
||||
fi
|
||||
am_display_PYTHON=$am_cv_pathless_PYTHON
|
||||
fi
|
||||
])
|
||||
|
||||
if test "$PYTHON" = :; then
|
||||
dnl Run any user-specified action, or abort.
|
||||
m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])])
|
||||
else
|
||||
|
||||
dnl Query Python for its version number. Although site.py simply uses
|
||||
dnl sys.version[:3], printing that failed with Python 3.10, since the
|
||||
dnl trailing zero was eliminated. So now we output just the major
|
||||
dnl and minor version numbers, as numbers. Apparently the tertiary
|
||||
dnl version is not of interest.
|
||||
dnl
|
||||
AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version],
|
||||
[am_cv_python_version=`$PYTHON -c "import sys; print ('%u.%u' % sys.version_info[[:2]])"`])
|
||||
AC_SUBST([PYTHON_VERSION], [$am_cv_python_version])
|
||||
|
||||
dnl At times, e.g., when building shared libraries, you may want
|
||||
dnl to know which OS platform Python thinks this is.
|
||||
dnl
|
||||
AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform],
|
||||
[am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`])
|
||||
AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform])
|
||||
|
||||
dnl emacs-page
|
||||
dnl If --with-python-sys-prefix is given, use the values of sys.prefix
|
||||
dnl and sys.exec_prefix for the corresponding values of PYTHON_PREFIX
|
||||
dnl and PYTHON_EXEC_PREFIX. Otherwise, use the GNU ${prefix} and
|
||||
dnl ${exec_prefix} variables.
|
||||
dnl
|
||||
dnl The two are made distinct variables so they can be overridden if
|
||||
dnl need be, although general consensus is that you shouldn't need
|
||||
dnl this separation.
|
||||
dnl
|
||||
dnl Also allow directly setting the prefixes via configure options,
|
||||
dnl overriding any default.
|
||||
dnl
|
||||
if test "x$prefix" = xNONE; then
|
||||
am__usable_prefix=$ac_default_prefix
|
||||
else
|
||||
am__usable_prefix=$prefix
|
||||
fi
|
||||
|
||||
# Allow user to request using sys.* values from Python,
|
||||
# instead of the GNU $prefix values.
|
||||
AC_ARG_WITH([python-sys-prefix],
|
||||
[AS_HELP_STRING([--with-python-sys-prefix],
|
||||
[use Python's sys.prefix and sys.exec_prefix values])],
|
||||
[am_use_python_sys=:],
|
||||
[am_use_python_sys=false])
|
||||
|
||||
# Allow user to override whatever the default Python prefix is.
|
||||
AC_ARG_WITH([python_prefix],
|
||||
[AS_HELP_STRING([--with-python_prefix],
|
||||
[override the default PYTHON_PREFIX])],
|
||||
[am_python_prefix_subst=$withval
|
||||
am_cv_python_prefix=$withval
|
||||
AC_MSG_CHECKING([for explicit $am_display_PYTHON prefix])
|
||||
AC_MSG_RESULT([$am_cv_python_prefix])],
|
||||
[
|
||||
if $am_use_python_sys; then
|
||||
# using python sys.prefix value, not GNU
|
||||
AC_CACHE_CHECK([for python default $am_display_PYTHON prefix],
|
||||
[am_cv_python_prefix],
|
||||
[am_cv_python_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.prefix)"`])
|
||||
|
||||
dnl If sys.prefix is a subdir of $prefix, replace the literal value of
|
||||
dnl $prefix with a variable reference so it can be overridden.
|
||||
case $am_cv_python_prefix in
|
||||
$am__usable_prefix*)
|
||||
am__strip_prefix=`echo "$am__usable_prefix" | sed 's|.|.|g'`
|
||||
am_python_prefix_subst=`echo "$am_cv_python_prefix" | sed "s,^$am__strip_prefix,\\${prefix},"`
|
||||
;;
|
||||
*)
|
||||
am_python_prefix_subst=$am_cv_python_prefix
|
||||
;;
|
||||
esac
|
||||
else # using GNU prefix value, not python sys.prefix
|
||||
am_python_prefix_subst='${prefix}'
|
||||
am_python_prefix=$am_python_prefix_subst
|
||||
AC_MSG_CHECKING([for GNU default $am_display_PYTHON prefix])
|
||||
AC_MSG_RESULT([$am_python_prefix])
|
||||
fi])
|
||||
# Substituting python_prefix_subst value.
|
||||
AC_SUBST([PYTHON_PREFIX], [$am_python_prefix_subst])
|
||||
|
||||
# emacs-page Now do it all over again for Python exec_prefix, but with yet
|
||||
# another conditional: fall back to regular prefix if that was specified.
|
||||
AC_ARG_WITH([python_exec_prefix],
|
||||
[AS_HELP_STRING([--with-python_exec_prefix],
|
||||
[override the default PYTHON_EXEC_PREFIX])],
|
||||
[am_python_exec_prefix_subst=$withval
|
||||
am_cv_python_exec_prefix=$withval
|
||||
AC_MSG_CHECKING([for explicit $am_display_PYTHON exec_prefix])
|
||||
AC_MSG_RESULT([$am_cv_python_exec_prefix])],
|
||||
[
|
||||
# no explicit --with-python_exec_prefix, but if
|
||||
# --with-python_prefix was given, use its value for python_exec_prefix too.
|
||||
AS_IF([test -n "$with_python_prefix"],
|
||||
[am_python_exec_prefix_subst=$with_python_prefix
|
||||
am_cv_python_exec_prefix=$with_python_prefix
|
||||
AC_MSG_CHECKING([for python_prefix-given $am_display_PYTHON exec_prefix])
|
||||
AC_MSG_RESULT([$am_cv_python_exec_prefix])],
|
||||
[
|
||||
# Set am__usable_exec_prefix whether using GNU or Python values,
|
||||
# since we use that variable for pyexecdir.
|
||||
if test "x$exec_prefix" = xNONE; then
|
||||
am__usable_exec_prefix=$am__usable_prefix
|
||||
else
|
||||
am__usable_exec_prefix=$exec_prefix
|
||||
fi
|
||||
#
|
||||
if $am_use_python_sys; then # using python sys.exec_prefix, not GNU
|
||||
AC_CACHE_CHECK([for python default $am_display_PYTHON exec_prefix],
|
||||
[am_cv_python_exec_prefix],
|
||||
[am_cv_python_exec_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.exec_prefix)"`])
|
||||
dnl If sys.exec_prefix is a subdir of $exec_prefix, replace the
|
||||
dnl literal value of $exec_prefix with a variable reference so it can
|
||||
dnl be overridden.
|
||||
case $am_cv_python_exec_prefix in
|
||||
$am__usable_exec_prefix*)
|
||||
am__strip_prefix=`echo "$am__usable_exec_prefix" | sed 's|.|.|g'`
|
||||
am_python_exec_prefix_subst=`echo "$am_cv_python_exec_prefix" | sed "s,^$am__strip_prefix,\\${exec_prefix},"`
|
||||
;;
|
||||
*)
|
||||
am_python_exec_prefix_subst=$am_cv_python_exec_prefix
|
||||
;;
|
||||
esac
|
||||
else # using GNU $exec_prefix, not python sys.exec_prefix
|
||||
am_python_exec_prefix_subst='${exec_prefix}'
|
||||
am_python_exec_prefix=$am_python_exec_prefix_subst
|
||||
AC_MSG_CHECKING([for GNU default $am_display_PYTHON exec_prefix])
|
||||
AC_MSG_RESULT([$am_python_exec_prefix])
|
||||
fi])])
|
||||
# Substituting python_exec_prefix_subst.
|
||||
AC_SUBST([PYTHON_EXEC_PREFIX], [$am_python_exec_prefix_subst])
|
||||
|
||||
# Factor out some code duplication into this shell variable.
|
||||
am_python_setup_sysconfig="\
|
||||
import sys
|
||||
# Prefer sysconfig over distutils.sysconfig, for better compatibility
|
||||
# with python 3.x. See automake bug#10227.
|
||||
try:
|
||||
import sysconfig
|
||||
except ImportError:
|
||||
can_use_sysconfig = 0
|
||||
else:
|
||||
can_use_sysconfig = 1
|
||||
# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs:
|
||||
# <https://github.com/pypa/virtualenv/issues/118>
|
||||
try:
|
||||
from platform import python_implementation
|
||||
if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7':
|
||||
can_use_sysconfig = 0
|
||||
except ImportError:
|
||||
pass" # end of am_python_setup_sysconfig
|
||||
|
||||
# More repeated code, for figuring out the installation scheme to use.
|
||||
am_python_setup_scheme="if hasattr(sysconfig, 'get_default_scheme'):
|
||||
scheme = sysconfig.get_default_scheme()
|
||||
else:
|
||||
scheme = sysconfig._get_default_scheme()
|
||||
if scheme == 'posix_local':
|
||||
if '$am_py_prefix' == '/usr':
|
||||
scheme = 'deb_system' # should only happen during Debian package builds
|
||||
else:
|
||||
# Debian's default scheme installs to /usr/local/ but we want to
|
||||
# follow the prefix, as we always have.
|
||||
# See bugs#54412, #64837, et al.
|
||||
scheme = 'posix_prefix'" # end of am_python_setup_scheme
|
||||
|
||||
dnl emacs-page Set up 4 directories:
|
||||
|
||||
dnl 1. pythondir: where to install python scripts. This is the
|
||||
dnl site-packages directory, not the python standard library
|
||||
dnl directory as in early automake betas. This behavior
|
||||
dnl is more consistent with lispdir.m4 for example.
|
||||
dnl Query sysconfig or distutils (per above) for this directory.
|
||||
dnl
|
||||
AC_CACHE_CHECK([for $am_display_PYTHON script directory (pythondir)],
|
||||
[am_cv_python_pythondir],
|
||||
[if test "x$am_cv_python_prefix" = x; then
|
||||
am_py_prefix=$am__usable_prefix
|
||||
else
|
||||
am_py_prefix=$am_cv_python_prefix
|
||||
fi
|
||||
am_cv_python_pythondir=`$PYTHON -c "
|
||||
$am_python_setup_sysconfig
|
||||
if can_use_sysconfig:
|
||||
try:
|
||||
$am_python_setup_scheme
|
||||
sitedir = sysconfig.get_path('purelib', scheme, vars={'base':'$am_py_prefix'})
|
||||
except:
|
||||
sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'})
|
||||
else:
|
||||
from distutils import sysconfig
|
||||
sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix')
|
||||
sys.stdout.write(sitedir)"`
|
||||
#
|
||||
case $am_cv_python_pythondir in
|
||||
$am_py_prefix*)
|
||||
am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'`
|
||||
am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,\\${PYTHON_PREFIX},"`
|
||||
;;
|
||||
*)
|
||||
case $am_py_prefix in
|
||||
/usr|/System*) ;;
|
||||
*) am_cv_python_pythondir="\${PYTHON_PREFIX}/lib/python$PYTHON_VERSION/site-packages"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
])
|
||||
AC_SUBST([pythondir], [$am_cv_python_pythondir])
|
||||
|
||||
dnl 2. pkgpythondir: $PACKAGE directory under pythondir. Was
|
||||
dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is
|
||||
dnl more consistent with the rest of automake.
|
||||
dnl
|
||||
AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE])
|
||||
|
||||
dnl 3. pyexecdir: directory for installing python extension modules
|
||||
dnl (shared libraries).
|
||||
dnl Query sysconfig or distutils for this directory.
|
||||
dnl Much of this is the same as for prefix setup above.
|
||||
dnl
|
||||
AC_CACHE_CHECK([for $am_display_PYTHON extension module directory (pyexecdir)],
|
||||
[am_cv_python_pyexecdir],
|
||||
[if test "x$am_cv_python_exec_prefix" = x; then
|
||||
am_py_exec_prefix=$am__usable_exec_prefix
|
||||
else
|
||||
am_py_exec_prefix=$am_cv_python_exec_prefix
|
||||
fi
|
||||
am_cv_python_pyexecdir=`$PYTHON -c "
|
||||
$am_python_setup_sysconfig
|
||||
if can_use_sysconfig:
|
||||
try:
|
||||
$am_python_setup_scheme
|
||||
sitedir = sysconfig.get_path('platlib', scheme, vars={'platbase':'$am_py_exec_prefix'})
|
||||
except:
|
||||
sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_exec_prefix'})
|
||||
else:
|
||||
from distutils import sysconfig
|
||||
sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_exec_prefix')
|
||||
sys.stdout.write(sitedir)"`
|
||||
#
|
||||
case $am_cv_python_pyexecdir in
|
||||
$am_py_exec_prefix*)
|
||||
am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'`
|
||||
am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,\\${PYTHON_EXEC_PREFIX},"`
|
||||
;;
|
||||
*)
|
||||
case $am_py_exec_prefix in
|
||||
/usr|/System*) ;;
|
||||
*) am_cv_python_pyexecdir="\${PYTHON_EXEC_PREFIX}/lib/python$PYTHON_VERSION/site-packages"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
])
|
||||
AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir])
|
||||
|
||||
dnl 4. pkgpyexecdir: $(pyexecdir)/$(PACKAGE)
|
||||
dnl
|
||||
AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE])
|
||||
|
||||
dnl Run any user-specified action.
|
||||
$2
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
|
||||
# ---------------------------------------------------------------------------
|
||||
# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION.
|
||||
# Run ACTION-IF-FALSE otherwise.
|
||||
# This test uses sys.hexversion instead of the string equivalent (first
|
||||
# word of sys.version), in order to cope with versions such as 2.2c1.
|
||||
# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000).
|
||||
AC_DEFUN([AM_PYTHON_CHECK_VERSION],
|
||||
[prog="import sys
|
||||
# split strings by '.' and convert to numeric. Append some zeros
|
||||
# because we need at least 4 digits for the hex conversion.
|
||||
# map returns an iterator in Python 3.0 and a list in 2.x
|
||||
minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]]
|
||||
minverhex = 0
|
||||
# xrange is not present in Python 3.0 and range returns an iterator
|
||||
for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]]
|
||||
sys.exit(sys.hexversion < minverhex)"
|
||||
AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])])
|
||||
|
||||
# Copyright (C) 2001-2024 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# AM_RUN_LOG(COMMAND)
|
||||
# -------------------
|
||||
# Run COMMAND, save the exit status in ac_status, and log it.
|
||||
# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
|
||||
AC_DEFUN([AM_RUN_LOG],
|
||||
[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
|
||||
($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
|
||||
ac_status=$?
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
|
||||
(exit $ac_status); }])
|
||||
|
||||
m4_include([acinclude.m4])
|
1768
autoconf/config.guess
vendored
Executable file
1768
autoconf/config.guess
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1890
autoconf/config.sub
vendored
Executable file
1890
autoconf/config.sub
vendored
Executable file
File diff suppressed because it is too large
Load Diff
541
autoconf/install-sh
Executable file
541
autoconf/install-sh
Executable file
@ -0,0 +1,541 @@
|
||||
#!/usr/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2020-11-14.01; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# 'make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
tab=' '
|
||||
nl='
|
||||
'
|
||||
IFS=" $tab$nl"
|
||||
|
||||
# Set DOITPROG to "echo" to test this script.
|
||||
|
||||
doit=${DOITPROG-}
|
||||
doit_exec=${doit:-exec}
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
# Create dirs (including intermediate dirs) using mode 755.
|
||||
# This is like GNU 'install' as of coreutils 8.32 (2020).
|
||||
mkdir_umask=22
|
||||
|
||||
backupsuffix=
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
is_target_a_directory=possibly
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-p pass -p to $cpprog.
|
||||
-s $stripprog installed files.
|
||||
-S SUFFIX attempt to back up existing files, with suffix SUFFIX.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
|
||||
By default, rm is invoked with -f; when overridden with RMPROG,
|
||||
it's up to you to specify -f if you want it.
|
||||
|
||||
If -S is not specified, no backups are attempted.
|
||||
|
||||
Email bug reports to bug-automake@gnu.org.
|
||||
Automake home page: https://www.gnu.org/software/automake/
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-p) cpprog="$cpprog -p";;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-S) backupsuffix="$2"
|
||||
shift;;
|
||||
|
||||
-t)
|
||||
is_target_a_directory=always
|
||||
dst_arg=$2
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) is_target_a_directory=never;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# We allow the use of options -d and -T together, by making -d
|
||||
# take the precedence; this is for compatibility with GNU install.
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
if test -n "$dst_arg"; then
|
||||
echo "$0: target directory not allowed when installing a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call 'install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||
if test ! -d "$dst_arg"; then
|
||||
echo "$0: $dst_arg: Is not a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $src in
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
# Don't chown directories that already exist.
|
||||
if test $dstdir_status = 0; then
|
||||
chowncmd=""
|
||||
fi
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst_arg
|
||||
|
||||
# If destination is a directory, append the input filename.
|
||||
if test -d "$dst"; then
|
||||
if test "$is_target_a_directory" = never; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dstbase=`basename "$src"`
|
||||
case $dst in
|
||||
*/) dst=$dst$dstbase;;
|
||||
*) dst=$dst/$dstbase;;
|
||||
esac
|
||||
dstdir_status=0
|
||||
else
|
||||
dstdir=`dirname "$dst"`
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
case $dstdir in
|
||||
*/) dstdirslash=$dstdir;;
|
||||
*) dstdirslash=$dstdir/;;
|
||||
esac
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
# The $RANDOM variable is not portable (e.g., dash). Use it
|
||||
# here however when possible just to lower collision chance.
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
|
||||
trap '
|
||||
ret=$?
|
||||
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
|
||||
exit $ret
|
||||
' 0
|
||||
|
||||
# Because "mkdir -p" follows existing symlinks and we likely work
|
||||
# directly in world-writeable /tmp, make sure that the '$tmpdir'
|
||||
# directory is successfully created first before we actually test
|
||||
# 'mkdir -p'.
|
||||
if (umask $mkdir_umask &&
|
||||
$mkdirprog $mkdir_mode "$tmpdir" &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
test_tmpdir="$tmpdir/a"
|
||||
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=${dstdirslash}_inst.$$_
|
||||
rmtmp=${dstdirslash}_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask &&
|
||||
{ test -z "$stripcmd" || {
|
||||
# Create $dsttmp read-write so that cp doesn't create it read-only,
|
||||
# which would cause strip to fail.
|
||||
if test -z "$doit"; then
|
||||
: >"$dsttmp" # No need to fork-exec 'touch'.
|
||||
else
|
||||
$doit touch "$dsttmp"
|
||||
fi
|
||||
}
|
||||
} &&
|
||||
$doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
set +f &&
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# If $backupsuffix is set, and the file being installed
|
||||
# already exists, attempt a backup. Don't worry if it fails,
|
||||
# e.g., if mv doesn't support -f.
|
||||
if test -n "$backupsuffix" && test -f "$dst"; then
|
||||
$doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
|
||||
fi
|
||||
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
242
autoconf/py-compile
Executable file
242
autoconf/py-compile
Executable file
@ -0,0 +1,242 @@
|
||||
#!/bin/sh
|
||||
# py-compile - Compile a Python program
|
||||
|
||||
scriptversion=2023-03-30.00; # UTC
|
||||
|
||||
# Copyright (C) 2000-2023 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
if test -z "$PYTHON"; then
|
||||
PYTHON=python
|
||||
fi
|
||||
|
||||
me=py-compile
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$me: $*" >&2
|
||||
echo "Try '$me --help' for more information." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
basedir=
|
||||
destdir=
|
||||
while test $# -ne 0; do
|
||||
case "$1" in
|
||||
--basedir)
|
||||
if test $# -lt 2; then
|
||||
usage_error "option '--basedir' requires an argument"
|
||||
else
|
||||
basedir=$2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--destdir)
|
||||
if test $# -lt 2; then
|
||||
usage_error "option '--destdir' requires an argument"
|
||||
else
|
||||
destdir=$2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
cat <<\EOF
|
||||
Usage: py-compile [options] FILES...
|
||||
|
||||
Byte compile some python scripts FILES. Use --destdir to specify any
|
||||
leading directory path to the FILES that you don't want to include in the
|
||||
byte compiled file. Specify --basedir for any additional path information you
|
||||
do want to be shown in the byte compiled file.
|
||||
|
||||
Options:
|
||||
--basedir DIR Prefix all FILES with DIR, and include in error messages.
|
||||
--destdir DIR Prefix all FILES with DIR before compiling.
|
||||
-v, --version Display version information.
|
||||
-h, --help This help screen.
|
||||
|
||||
Example:
|
||||
py-compile --destdir /tmp/pkg-root --basedir /usr/share/test test.py test2.py
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v|--version)
|
||||
echo "$me $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage_error "unrecognized option '$1'"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test $# -eq 0; then
|
||||
usage_error "no files given"
|
||||
fi
|
||||
|
||||
# if basedir was given, then it should be prepended to filenames before
|
||||
# byte compilation.
|
||||
if test -z "$basedir"; then
|
||||
pathtrans="path = file"
|
||||
else
|
||||
pathtrans="path = os.path.join('$basedir', file)"
|
||||
fi
|
||||
|
||||
# if destdir was given, then it needs to be prepended to the filename to
|
||||
# byte compile but not go into the compiled file.
|
||||
if test -z "$destdir"; then
|
||||
filetrans="filepath = path"
|
||||
else
|
||||
filetrans="filepath = os.path.normpath('$destdir' + os.sep + path)"
|
||||
fi
|
||||
|
||||
python_major=`$PYTHON -c 'import sys; print(sys.version_info[0])'`
|
||||
if test -z "$python_major"; then
|
||||
usage_error "could not determine $PYTHON major version"
|
||||
fi
|
||||
|
||||
case $python_major in
|
||||
[01])
|
||||
usage_error "python version 0.x and 1.x not supported"
|
||||
;;
|
||||
esac
|
||||
|
||||
python_minor=`$PYTHON -c 'import sys; print(sys.version_info[1])'`
|
||||
|
||||
# NB: When adding support for newer versions, prefer copying & adding new cases
|
||||
# rather than try to keep things merged with shell variables.
|
||||
|
||||
# First byte compile (no optimization) all the modules.
|
||||
# This works for all currently known Python versions.
|
||||
$PYTHON -c "
|
||||
import sys, os, py_compile
|
||||
|
||||
try:
|
||||
import importlib
|
||||
except ImportError:
|
||||
importlib = None
|
||||
|
||||
# importlib.util.cache_from_source was added in 3.4
|
||||
if (
|
||||
hasattr(importlib, 'util')
|
||||
and hasattr(importlib.util, 'cache_from_source')
|
||||
):
|
||||
destpath = importlib.util.cache_from_source
|
||||
else:
|
||||
destpath = lambda filepath: filepath + 'c'
|
||||
|
||||
sys.stdout.write('Byte-compiling python modules...\n')
|
||||
for file in sys.argv[1:]:
|
||||
$pathtrans
|
||||
$filetrans
|
||||
if (
|
||||
not os.path.exists(filepath)
|
||||
or not (len(filepath) >= 3 and filepath[-3:] == '.py')
|
||||
):
|
||||
continue
|
||||
sys.stdout.write(file + ' ')
|
||||
sys.stdout.flush()
|
||||
py_compile.compile(filepath, destpath(filepath), path)
|
||||
sys.stdout.write('\n')" "$@" || exit $?
|
||||
|
||||
# Then byte compile w/optimization all the modules.
|
||||
$PYTHON -O -c "
|
||||
import sys, os, py_compile
|
||||
|
||||
try:
|
||||
import importlib
|
||||
except ImportError:
|
||||
importlib = None
|
||||
|
||||
# importlib.util.cache_from_source was added in 3.4
|
||||
if (
|
||||
hasattr(importlib, 'util')
|
||||
and hasattr(importlib.util, 'cache_from_source')
|
||||
):
|
||||
destpath = importlib.util.cache_from_source
|
||||
else:
|
||||
destpath = lambda filepath: filepath + 'o'
|
||||
|
||||
# pypy2 does not use .pyo optimization
|
||||
if sys.version_info.major <= 2 and hasattr(sys, 'pypy_translation_info'):
|
||||
sys.exit(0)
|
||||
|
||||
sys.stdout.write('Byte-compiling python modules (optimized versions) ...\n')
|
||||
for file in sys.argv[1:]:
|
||||
$pathtrans
|
||||
$filetrans
|
||||
if (
|
||||
not os.path.exists(filepath)
|
||||
or not (len(filepath) >= 3 and filepath[-3:] == '.py')
|
||||
):
|
||||
continue
|
||||
sys.stdout.write(file + ' ')
|
||||
sys.stdout.flush()
|
||||
py_compile.compile(filepath, destpath(filepath), path)
|
||||
sys.stdout.write('\n')" "$@" 2>/dev/null || exit $?
|
||||
|
||||
# Then byte compile w/more optimization.
|
||||
# Only do this for Python 3.5+, see https://bugs.gnu.org/38043 for background.
|
||||
case $python_major.$python_minor in
|
||||
2.*|3.[0-4])
|
||||
;;
|
||||
*)
|
||||
$PYTHON -OO -c "
|
||||
import sys, os, py_compile, importlib
|
||||
|
||||
sys.stdout.write('Byte-compiling python modules (more optimized versions)'
|
||||
' ...\n')
|
||||
for file in sys.argv[1:]:
|
||||
$pathtrans
|
||||
$filetrans
|
||||
if (
|
||||
not os.path.exists(filepath)
|
||||
or not (len(filepath) >= 3 and filepath[-3:] == '.py')
|
||||
):
|
||||
continue
|
||||
sys.stdout.write(file + ' ')
|
||||
sys.stdout.flush()
|
||||
py_compile.compile(filepath, importlib.util.cache_from_source(filepath), path)
|
||||
sys.stdout.write('\n')" "$@" 2>/dev/null || exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
40
base/Makefile
Normal file
40
base/Makefile
Normal file
@ -0,0 +1,40 @@
|
||||
# Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of the device-mapper userspace tools.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU Lesser General Public License v.2.1.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# Uncomment this to build the simple radix tree. You'll need to make clean too.
|
||||
# Comment to build the advanced radix tree.
|
||||
#base/data-struct/radix-tree.o: CFLAGS += -DSIMPLE_RADIX_TREE
|
||||
|
||||
# NOTE: this Makefile only works as 'include' for toplevel Makefile
|
||||
# which defined all top_* variables
|
||||
|
||||
BASE_SOURCE=\
|
||||
base/data-struct/hash.c \
|
||||
base/data-struct/list.c \
|
||||
base/data-struct/radix-tree.c
|
||||
|
||||
BASE_TARGET = base/libbase.a
|
||||
BASE_DEPENDS = $(BASE_SOURCE:%.c=%.d)
|
||||
BASE_OBJECTS = $(BASE_SOURCE:%.c=%.o)
|
||||
CLEAN_TARGETS += $(BASE_DEPENDS) $(BASE_OBJECTS) \
|
||||
$(BASE_SOURCE:%.c=%.gcda) \
|
||||
$(BASE_SOURCE:%.c=%.gcno) \
|
||||
$(BASE_TARGET)
|
||||
|
||||
$(BASE_TARGET): $(BASE_OBJECTS)
|
||||
$(SHOW) " [AR] $@"
|
||||
$(Q) $(RM) $@
|
||||
$(Q) $(AR) rsv $@ $(BASE_OBJECTS) > /dev/null
|
||||
|
||||
ifeq ("$(USE_TRACKING)","yes")
|
||||
-include $(BASE_DEPENDS)
|
||||
endif
|
477
base/data-struct/hash.c
Normal file
477
base/data-struct/hash.c
Normal file
@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "device_mapper/misc/dmlib.h"
|
||||
#include "base/memory/zalloc.h"
|
||||
#include "hash.h"
|
||||
|
||||
struct dm_hash_node {
|
||||
struct dm_hash_node *next;
|
||||
void *data;
|
||||
unsigned data_len;
|
||||
unsigned keylen;
|
||||
unsigned hash;
|
||||
char key[0];
|
||||
};
|
||||
|
||||
struct dm_hash_table {
|
||||
unsigned num_nodes;
|
||||
unsigned num_hint;
|
||||
unsigned mask_slots; /* (slots - 1) -> used as hash mask */
|
||||
unsigned collisions; /* Collisions of hash keys */
|
||||
unsigned search; /* How many keys were searched */
|
||||
unsigned found; /* How many nodes were found */
|
||||
unsigned same_hash; /* Was there a collision with same masked hash and len ? */
|
||||
struct dm_hash_node **slots;
|
||||
};
|
||||
|
||||
#if 0 /* TO BE REMOVED */
|
||||
static unsigned _hash(const void *key, unsigned len)
|
||||
{
|
||||
/* Permutation of the Integers 0 through 255 */
|
||||
static const unsigned char _nums[] = {
|
||||
1, 14, 110, 25, 97, 174, 132, 119, 138, 170, 125, 118, 27, 233, 140, 51,
|
||||
87, 197, 177, 107, 234, 169, 56, 68, 30, 7, 173, 73, 188, 40, 36, 65,
|
||||
49, 213, 104, 190, 57, 211, 148, 223, 48, 115, 15, 2, 67, 186, 210, 28,
|
||||
12, 181, 103, 70, 22, 58, 75, 78, 183, 167, 238, 157, 124, 147, 172,
|
||||
144,
|
||||
176, 161, 141, 86, 60, 66, 128, 83, 156, 241, 79, 46, 168, 198, 41, 254,
|
||||
178, 85, 253, 237, 250, 154, 133, 88, 35, 206, 95, 116, 252, 192, 54,
|
||||
221,
|
||||
102, 218, 255, 240, 82, 106, 158, 201, 61, 3, 89, 9, 42, 155, 159, 93,
|
||||
166, 80, 50, 34, 175, 195, 100, 99, 26, 150, 16, 145, 4, 33, 8, 189,
|
||||
121, 64, 77, 72, 208, 245, 130, 122, 143, 55, 105, 134, 29, 164, 185,
|
||||
194,
|
||||
193, 239, 101, 242, 5, 171, 126, 11, 74, 59, 137, 228, 108, 191, 232,
|
||||
139,
|
||||
6, 24, 81, 20, 127, 17, 91, 92, 251, 151, 225, 207, 21, 98, 113, 112,
|
||||
84, 226, 18, 214, 199, 187, 13, 32, 94, 220, 224, 212, 247, 204, 196,
|
||||
43,
|
||||
249, 236, 45, 244, 111, 182, 153, 136, 129, 90, 217, 202, 19, 165, 231,
|
||||
71,
|
||||
230, 142, 96, 227, 62, 179, 246, 114, 162, 53, 160, 215, 205, 180, 47,
|
||||
109,
|
||||
44, 38, 31, 149, 135, 0, 216, 52, 63, 23, 37, 69, 39, 117, 146, 184,
|
||||
163, 200, 222, 235, 248, 243, 219, 10, 152, 131, 123, 229, 203, 76, 120,
|
||||
209
|
||||
};
|
||||
|
||||
const uint8_t *str = key;
|
||||
unsigned h = 0, g;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
h <<= 4;
|
||||
h += _nums[*str++];
|
||||
g = h & ((unsigned) 0xf << 16u);
|
||||
if (g) {
|
||||
h ^= g >> 16u;
|
||||
h ^= g >> 5u;
|
||||
}
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/* In-kernel DM hashing, still lots of collisions */
|
||||
static unsigned _hash_in_kernel(const char *key, unsigned len)
|
||||
{
|
||||
const unsigned char *str = (unsigned char *)key;
|
||||
const unsigned hash_mult = 2654435387U;
|
||||
unsigned hash = 0, i;
|
||||
|
||||
for (i = 0; i < len; ++i)
|
||||
hash = (hash + str[i]) * hash_mult;
|
||||
|
||||
return hash;
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef get16bits
|
||||
#if (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))
|
||||
#define get16bits(d) (*((const uint16_t *) (d)))
|
||||
#endif
|
||||
|
||||
#if !defined (get16bits)
|
||||
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
|
||||
+(uint32_t)(((const uint8_t *)(d))[0]) )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Adapted Bob Jenkins hash to read by 2 bytes if possible.
|
||||
* https://secure.wikimedia.org/wikipedia/en/wiki/Jenkins_hash_function
|
||||
*
|
||||
* Reduces amount of hash collisions
|
||||
*/
|
||||
static unsigned _hash(const void *key, unsigned len)
|
||||
{
|
||||
const uint8_t *str = (uint8_t*) key;
|
||||
unsigned hash = 0, i;
|
||||
unsigned sz = len / 2;
|
||||
|
||||
for(i = 0; i < sz; ++i) {
|
||||
hash += get16bits(str + 2 * i);
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
if (len & 1) {
|
||||
hash += str[len - 1];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static struct dm_hash_node *_create_node(const void *key, unsigned len)
|
||||
{
|
||||
struct dm_hash_node *n = malloc(sizeof(*n) + len);
|
||||
|
||||
if (n) {
|
||||
memcpy(n->key, key, len);
|
||||
n->keylen = len;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
struct dm_hash_table *dm_hash_create(unsigned size_hint)
|
||||
{
|
||||
size_t len;
|
||||
unsigned new_size = 16u;
|
||||
struct dm_hash_table *hc = zalloc(sizeof(*hc));
|
||||
|
||||
if (!hc) {
|
||||
log_error("Failed to allocate memory for hash.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
hc->num_hint = size_hint;
|
||||
|
||||
/* round size hint up to a power of two */
|
||||
while (new_size < size_hint)
|
||||
new_size = new_size << 1;
|
||||
|
||||
hc->mask_slots = new_size - 1;
|
||||
len = sizeof(*(hc->slots)) * new_size;
|
||||
if (!(hc->slots = zalloc(len))) {
|
||||
free(hc);
|
||||
log_error("Failed to allocate slots for hash.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hc;
|
||||
}
|
||||
|
||||
static void _free_nodes(struct dm_hash_table *t)
|
||||
{
|
||||
struct dm_hash_node *c, *n;
|
||||
unsigned i;
|
||||
|
||||
#ifdef DEBUG
|
||||
log_debug("Free hash hint:%d slots:%d nodes:%d (s:%d f:%d c:%d h:%d)",
|
||||
t->num_hint, t->mask_slots + 1, t->num_nodes,
|
||||
t->search, t->found, t->collisions, t->same_hash);
|
||||
#endif
|
||||
|
||||
if (!t->num_nodes)
|
||||
return;
|
||||
|
||||
for (i = 0; i <= t->mask_slots; i++)
|
||||
for (c = t->slots[i]; c; c = n) {
|
||||
n = c->next;
|
||||
free(c);
|
||||
}
|
||||
}
|
||||
|
||||
void dm_hash_destroy(struct dm_hash_table *t)
|
||||
{
|
||||
_free_nodes(t);
|
||||
free(t->slots);
|
||||
free(t);
|
||||
}
|
||||
|
||||
static struct dm_hash_node **_findh(struct dm_hash_table *t, const void *key,
|
||||
uint32_t len, unsigned hash)
|
||||
{
|
||||
struct dm_hash_node **c;
|
||||
|
||||
++t->search;
|
||||
for (c = &t->slots[hash & t->mask_slots]; *c; c = &((*c)->next)) {
|
||||
if ((*c)->keylen == len && (*c)->hash == hash) {
|
||||
if (!memcmp(key, (*c)->key, len)) {
|
||||
++t->found;
|
||||
break;
|
||||
}
|
||||
++t->same_hash;
|
||||
}
|
||||
++t->collisions;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static struct dm_hash_node **_find(struct dm_hash_table *t, const void *key,
|
||||
uint32_t len)
|
||||
{
|
||||
return _findh(t, key, len, _hash(key, len));
|
||||
}
|
||||
|
||||
void *dm_hash_lookup_binary(struct dm_hash_table *t, const void *key,
|
||||
uint32_t len)
|
||||
{
|
||||
struct dm_hash_node **c = _find(t, key, len);
|
||||
|
||||
return *c ? (*c)->data : 0;
|
||||
}
|
||||
|
||||
int dm_hash_insert_binary(struct dm_hash_table *t, const void *key,
|
||||
uint32_t len, void *data)
|
||||
{
|
||||
unsigned hash = _hash(key, len);
|
||||
struct dm_hash_node **c = _findh(t, key, len, hash);
|
||||
|
||||
if (*c)
|
||||
(*c)->data = data;
|
||||
else {
|
||||
struct dm_hash_node *n = _create_node(key, len);
|
||||
|
||||
if (!n)
|
||||
return 0;
|
||||
|
||||
n->data = data;
|
||||
n->hash = hash;
|
||||
n->next = 0;
|
||||
*c = n;
|
||||
t->num_nodes++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dm_hash_remove_binary(struct dm_hash_table *t, const void *key,
|
||||
uint32_t len)
|
||||
{
|
||||
struct dm_hash_node **c = _find(t, key, len);
|
||||
|
||||
if (*c) {
|
||||
struct dm_hash_node *old = *c;
|
||||
*c = (*c)->next;
|
||||
free(old);
|
||||
t->num_nodes--;
|
||||
}
|
||||
}
|
||||
|
||||
void *dm_hash_lookup(struct dm_hash_table *t, const char *key)
|
||||
{
|
||||
return dm_hash_lookup_binary(t, key, strlen(key) + 1);
|
||||
}
|
||||
|
||||
int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data)
|
||||
{
|
||||
return dm_hash_insert_binary(t, key, strlen(key) + 1, data);
|
||||
}
|
||||
|
||||
void dm_hash_remove(struct dm_hash_table *t, const char *key)
|
||||
{
|
||||
dm_hash_remove_binary(t, key, strlen(key) + 1);
|
||||
}
|
||||
|
||||
static struct dm_hash_node **_find_str_with_val(struct dm_hash_table *t,
|
||||
const void *key, const void *val,
|
||||
uint32_t len, uint32_t val_len)
|
||||
{
|
||||
struct dm_hash_node **c;
|
||||
unsigned h;
|
||||
|
||||
h = _hash(key, len) & t->mask_slots;
|
||||
|
||||
for (c = &t->slots[h]; *c; c = &((*c)->next)) {
|
||||
if ((*c)->keylen != len)
|
||||
continue;
|
||||
|
||||
if (!memcmp(key, (*c)->key, len) && (*c)->data) {
|
||||
if (((*c)->data_len == val_len) &&
|
||||
!memcmp(val, (*c)->data, val_len))
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dm_hash_insert_allow_multiple(struct dm_hash_table *t, const char *key,
|
||||
const void *val, uint32_t val_len)
|
||||
{
|
||||
struct dm_hash_node *n;
|
||||
struct dm_hash_node *first;
|
||||
int len = strlen(key) + 1;
|
||||
unsigned h;
|
||||
|
||||
n = _create_node(key, len);
|
||||
if (!n)
|
||||
return 0;
|
||||
|
||||
n->data = (void *)val;
|
||||
n->data_len = val_len;
|
||||
|
||||
h = _hash(key, len) & t->mask_slots;
|
||||
|
||||
first = t->slots[h];
|
||||
|
||||
if (first)
|
||||
n->next = first;
|
||||
else
|
||||
n->next = 0;
|
||||
t->slots[h] = n;
|
||||
|
||||
t->num_nodes++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look through multiple entries with the same key for one that has a
|
||||
* matching val and return that. If none have matching val, return NULL.
|
||||
*/
|
||||
void *dm_hash_lookup_with_val(struct dm_hash_table *t, const char *key,
|
||||
const void *val, uint32_t val_len)
|
||||
{
|
||||
struct dm_hash_node **c;
|
||||
|
||||
c = _find_str_with_val(t, key, val, strlen(key) + 1, val_len);
|
||||
|
||||
return (c && *c) ? (*c)->data : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look through multiple entries with the same key for one that has a
|
||||
* matching val and remove that.
|
||||
*/
|
||||
void dm_hash_remove_with_val(struct dm_hash_table *t, const char *key,
|
||||
const void *val, uint32_t val_len)
|
||||
{
|
||||
struct dm_hash_node **c;
|
||||
|
||||
c = _find_str_with_val(t, key, val, strlen(key) + 1, val_len);
|
||||
|
||||
if (c && *c) {
|
||||
struct dm_hash_node *old = *c;
|
||||
*c = (*c)->next;
|
||||
free(old);
|
||||
t->num_nodes--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up the value for a key and count how many
|
||||
* entries have the same key.
|
||||
*
|
||||
* If no entries have key, return NULL and set count to 0.
|
||||
*
|
||||
* If one entry has the key, the function returns the val,
|
||||
* and sets count to 1.
|
||||
*
|
||||
* If N entries have the key, the function returns the val
|
||||
* from the first entry, and sets count to N.
|
||||
*/
|
||||
void *dm_hash_lookup_with_count(struct dm_hash_table *t, const char *key, int *count)
|
||||
{
|
||||
struct dm_hash_node **c;
|
||||
struct dm_hash_node **c1 = NULL;
|
||||
uint32_t len = strlen(key) + 1;
|
||||
unsigned h;
|
||||
|
||||
*count = 0;
|
||||
|
||||
h = _hash(key, len) & t->mask_slots;
|
||||
|
||||
for (c = &t->slots[h]; *c; c = &((*c)->next)) {
|
||||
if ((*c)->keylen != len)
|
||||
continue;
|
||||
|
||||
if (!memcmp(key, (*c)->key, len)) {
|
||||
(*count)++;
|
||||
if (!c1)
|
||||
c1 = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c1)
|
||||
return NULL;
|
||||
else
|
||||
return *c1 ? (*c1)->data : 0;
|
||||
}
|
||||
|
||||
unsigned dm_hash_get_num_entries(struct dm_hash_table *t)
|
||||
{
|
||||
return t->num_nodes;
|
||||
}
|
||||
|
||||
void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f)
|
||||
{
|
||||
struct dm_hash_node *c, *n;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i <= t->mask_slots; i++)
|
||||
for (c = t->slots[i]; c; c = n) {
|
||||
n = c->next;
|
||||
f(c->data);
|
||||
}
|
||||
}
|
||||
|
||||
void dm_hash_wipe(struct dm_hash_table *t)
|
||||
{
|
||||
_free_nodes(t);
|
||||
memset(t->slots, 0, sizeof(struct dm_hash_node *) * (t->mask_slots + 1));
|
||||
t->num_nodes = t->collisions = t->search = t->same_hash = 0u;
|
||||
}
|
||||
|
||||
char *dm_hash_get_key(struct dm_hash_table *t __attribute__((unused)),
|
||||
struct dm_hash_node *n)
|
||||
{
|
||||
return n->key;
|
||||
}
|
||||
|
||||
void *dm_hash_get_data(struct dm_hash_table *t __attribute__((unused)),
|
||||
struct dm_hash_node *n)
|
||||
{
|
||||
return n->data;
|
||||
}
|
||||
|
||||
static struct dm_hash_node *_next_slot(struct dm_hash_table *t, unsigned s)
|
||||
{
|
||||
struct dm_hash_node *c = NULL;
|
||||
unsigned i;
|
||||
|
||||
for (i = s; i <= t->mask_slots && !c; i++)
|
||||
c = t->slots[i];
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t)
|
||||
{
|
||||
return _next_slot(t, 0);
|
||||
}
|
||||
|
||||
struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n)
|
||||
{
|
||||
return n->next ? n->next : _next_slot(t, (n->hash & t->mask_slots) + 1);
|
||||
}
|
94
base/data-struct/hash.h
Normal file
94
base/data-struct/hash.h
Normal file
@ -0,0 +1,94 @@
|
||||
#ifndef BASE_DATA_STRUCT_HASH_H
|
||||
#define BASE_DATA_STRUCT_HASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct dm_hash_table;
|
||||
struct dm_hash_node;
|
||||
|
||||
typedef void (*dm_hash_iterate_fn) (void *data);
|
||||
|
||||
struct dm_hash_table *dm_hash_create(unsigned size_hint)
|
||||
__attribute__((__warn_unused_result__));
|
||||
void dm_hash_destroy(struct dm_hash_table *t);
|
||||
void dm_hash_wipe(struct dm_hash_table *t);
|
||||
|
||||
void *dm_hash_lookup(struct dm_hash_table *t, const char *key);
|
||||
int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data);
|
||||
void dm_hash_remove(struct dm_hash_table *t, const char *key);
|
||||
|
||||
void *dm_hash_lookup_binary(struct dm_hash_table *t, const void *key, uint32_t len);
|
||||
int dm_hash_insert_binary(struct dm_hash_table *t, const void *key, uint32_t len,
|
||||
void *data);
|
||||
void dm_hash_remove_binary(struct dm_hash_table *t, const void *key, uint32_t len);
|
||||
|
||||
unsigned dm_hash_get_num_entries(struct dm_hash_table *t);
|
||||
void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f);
|
||||
|
||||
char *dm_hash_get_key(struct dm_hash_table *t, struct dm_hash_node *n);
|
||||
void *dm_hash_get_data(struct dm_hash_table *t, struct dm_hash_node *n);
|
||||
struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t);
|
||||
struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n);
|
||||
|
||||
/*
|
||||
* dm_hash_insert() replaces the value of an existing
|
||||
* entry with a matching key if one exists. Otherwise
|
||||
* it adds a new entry.
|
||||
*
|
||||
* dm_hash_insert_with_val() inserts a new entry if
|
||||
* another entry with the same key already exists.
|
||||
* val_len is the size of the data being inserted.
|
||||
*
|
||||
* If two entries with the same key exist,
|
||||
* (added using dm_hash_insert_allow_multiple), then:
|
||||
* . dm_hash_lookup() returns the first one it finds, and
|
||||
* dm_hash_lookup_with_val() returns the one with a matching
|
||||
* val_len/val.
|
||||
* . dm_hash_remove() removes the first one it finds, and
|
||||
* dm_hash_remove_with_val() removes the one with a matching
|
||||
* val_len/val.
|
||||
*
|
||||
* If a single entry with a given key exists, and it has
|
||||
* zero val_len, then:
|
||||
* . dm_hash_lookup() returns it
|
||||
* . dm_hash_lookup_with_val(val_len=0) returns it
|
||||
* . dm_hash_remove() removes it
|
||||
* . dm_hash_remove_with_val(val_len=0) removes it
|
||||
*
|
||||
* dm_hash_lookup_with_count() is a single call that will
|
||||
* both lookup a key's value and check if there is more
|
||||
* than one entry with the given key.
|
||||
*
|
||||
* (It is not meant to retrieve all the entries with the
|
||||
* given key. In the common case where a single entry exists
|
||||
* for the key, it is useful to have a single call that will
|
||||
* both look up the value and indicate if multiple values
|
||||
* exist for the key.)
|
||||
*
|
||||
* dm_hash_lookup_with_count:
|
||||
* . If no entries exist, the function returns NULL, and
|
||||
* the count is set to 0.
|
||||
* . If only one entry exists, the value of that entry is
|
||||
* returned and count is set to 1.
|
||||
* . If N entries exists, the value of the first entry is
|
||||
* returned and count is set to N.
|
||||
*/
|
||||
|
||||
void *dm_hash_lookup_with_val(struct dm_hash_table *t, const char *key,
|
||||
const void *val, uint32_t val_len);
|
||||
void dm_hash_remove_with_val(struct dm_hash_table *t, const char *key,
|
||||
const void *val, uint32_t val_len);
|
||||
int dm_hash_insert_allow_multiple(struct dm_hash_table *t, const char *key,
|
||||
const void *val, uint32_t val_len);
|
||||
void *dm_hash_lookup_with_count(struct dm_hash_table *t, const char *key, int *count);
|
||||
|
||||
|
||||
#define dm_hash_iterate(v, h) \
|
||||
for (v = dm_hash_get_first((h)); v; \
|
||||
v = dm_hash_get_next((h), v))
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
170
base/data-struct/list.c
Normal file
170
base/data-struct/list.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Initialise a list before use.
|
||||
* The list head's next and previous pointers point back to itself.
|
||||
*/
|
||||
void dm_list_init(struct dm_list *head)
|
||||
{
|
||||
head->n = head->p = head;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert an element before 'head'.
|
||||
* If 'head' is the list head, this adds an element to the end of the list.
|
||||
*/
|
||||
void dm_list_add(struct dm_list *head, struct dm_list *elem)
|
||||
{
|
||||
assert(head->n);
|
||||
|
||||
elem->n = head;
|
||||
elem->p = head->p;
|
||||
|
||||
head->p->n = elem;
|
||||
head->p = elem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert an element after 'head'.
|
||||
* If 'head' is the list head, this adds an element to the front of the list.
|
||||
*/
|
||||
void dm_list_add_h(struct dm_list *head, struct dm_list *elem)
|
||||
{
|
||||
assert(head->n);
|
||||
|
||||
elem->n = head->n;
|
||||
elem->p = head;
|
||||
|
||||
head->n->p = elem;
|
||||
head->n = elem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete an element from its list.
|
||||
* Note that this doesn't change the element itself - it may still be safe
|
||||
* to follow its pointers.
|
||||
*/
|
||||
void dm_list_del(struct dm_list *elem)
|
||||
{
|
||||
elem->n->p = elem->p;
|
||||
elem->p->n = elem->n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove an element from existing list and insert before 'head'.
|
||||
*/
|
||||
void dm_list_move(struct dm_list *head, struct dm_list *elem)
|
||||
{
|
||||
dm_list_del(elem);
|
||||
dm_list_add(head, elem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the list empty?
|
||||
*/
|
||||
int dm_list_empty(const struct dm_list *head)
|
||||
{
|
||||
return head->n == head;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is this the first element of the list?
|
||||
*/
|
||||
int dm_list_start(const struct dm_list *head, const struct dm_list *elem)
|
||||
{
|
||||
return elem->p == head;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is this the last element of the list?
|
||||
*/
|
||||
int dm_list_end(const struct dm_list *head, const struct dm_list *elem)
|
||||
{
|
||||
return elem->n == head;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return first element of the list or NULL if empty
|
||||
*/
|
||||
struct dm_list *dm_list_first(const struct dm_list *head)
|
||||
{
|
||||
return (dm_list_empty(head) ? NULL : head->n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return last element of the list or NULL if empty
|
||||
*/
|
||||
struct dm_list *dm_list_last(const struct dm_list *head)
|
||||
{
|
||||
return (dm_list_empty(head) ? NULL : head->p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the previous element of the list, or NULL if we've reached the start.
|
||||
*/
|
||||
struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem)
|
||||
{
|
||||
return (dm_list_start(head, elem) ? NULL : elem->p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the next element of the list, or NULL if we've reached the end.
|
||||
*/
|
||||
struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem)
|
||||
{
|
||||
return (dm_list_end(head, elem) ? NULL : elem->n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of elements in a list by walking it.
|
||||
*/
|
||||
unsigned int dm_list_size(const struct dm_list *head)
|
||||
{
|
||||
unsigned int s = 0;
|
||||
const struct dm_list *v;
|
||||
|
||||
dm_list_iterate(v, head)
|
||||
s++;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Join two lists together.
|
||||
* This moves all the elements of the list 'head1' to the end of the list
|
||||
* 'head', leaving 'head1' empty.
|
||||
*/
|
||||
void dm_list_splice(struct dm_list *head, struct dm_list *head1)
|
||||
{
|
||||
assert(head->n);
|
||||
assert(head1->n);
|
||||
|
||||
if (dm_list_empty(head1))
|
||||
return;
|
||||
|
||||
head1->p->n = head;
|
||||
head1->n->p = head->p;
|
||||
|
||||
head->p->n = head1->n;
|
||||
head->p = head1->p;
|
||||
|
||||
dm_list_init(head1);
|
||||
}
|
211
base/data-struct/list.h
Normal file
211
base/data-struct/list.h
Normal file
@ -0,0 +1,211 @@
|
||||
#ifndef BASE_DATA_STRUCT_LIST_H
|
||||
#define BASE_DATA_STRUCT_LIST_H
|
||||
|
||||
#include "base/memory/container_of.h"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* A list consists of a list head plus elements.
|
||||
* Each element has 'next' and 'previous' pointers.
|
||||
* The list head's pointers point to the first and the last element.
|
||||
*/
|
||||
|
||||
struct dm_list {
|
||||
struct dm_list *n, *p;
|
||||
};
|
||||
|
||||
/*
|
||||
* String list.
|
||||
*/
|
||||
struct dm_str_list {
|
||||
struct dm_list list;
|
||||
const char *str;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialise a list before use.
|
||||
* The list head's next and previous pointers point back to itself.
|
||||
*/
|
||||
#define DM_LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
#define DM_LIST_INIT(name) struct dm_list name = DM_LIST_HEAD_INIT(name)
|
||||
void dm_list_init(struct dm_list *head);
|
||||
|
||||
/*
|
||||
* Insert an element before 'head'.
|
||||
* If 'head' is the list head, this adds an element to the end of the list.
|
||||
*/
|
||||
void dm_list_add(struct dm_list *head, struct dm_list *elem);
|
||||
|
||||
/*
|
||||
* Insert an element after 'head'.
|
||||
* If 'head' is the list head, this adds an element to the front of the list.
|
||||
*/
|
||||
void dm_list_add_h(struct dm_list *head, struct dm_list *elem);
|
||||
|
||||
/*
|
||||
* Delete an element from its list.
|
||||
* Note that this doesn't change the element itself - it may still be safe
|
||||
* to follow its pointers.
|
||||
*/
|
||||
void dm_list_del(struct dm_list *elem);
|
||||
|
||||
/*
|
||||
* Remove an element from existing list and insert before 'head'.
|
||||
*/
|
||||
void dm_list_move(struct dm_list *head, struct dm_list *elem);
|
||||
|
||||
/*
|
||||
* Join 'head1' to the end of 'head'.
|
||||
*/
|
||||
void dm_list_splice(struct dm_list *head, struct dm_list *head1);
|
||||
|
||||
/*
|
||||
* Is the list empty?
|
||||
*/
|
||||
int dm_list_empty(const struct dm_list *head);
|
||||
|
||||
/*
|
||||
* Is this the first element of the list?
|
||||
*/
|
||||
int dm_list_start(const struct dm_list *head, const struct dm_list *elem);
|
||||
|
||||
/*
|
||||
* Is this the last element of the list?
|
||||
*/
|
||||
int dm_list_end(const struct dm_list *head, const struct dm_list *elem);
|
||||
|
||||
/*
|
||||
* Return first element of the list or NULL if empty
|
||||
*/
|
||||
struct dm_list *dm_list_first(const struct dm_list *head);
|
||||
|
||||
/*
|
||||
* Return last element of the list or NULL if empty
|
||||
*/
|
||||
struct dm_list *dm_list_last(const struct dm_list *head);
|
||||
|
||||
/*
|
||||
* Return the previous element of the list, or NULL if we've reached the start.
|
||||
*/
|
||||
struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem);
|
||||
|
||||
/*
|
||||
* Return the next element of the list, or NULL if we've reached the end.
|
||||
*/
|
||||
struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem);
|
||||
|
||||
/*
|
||||
* Given the address v of an instance of 'struct dm_list' called 'head'
|
||||
* contained in a structure of type t, return the containing structure.
|
||||
*/
|
||||
#define dm_list_struct_base(v, t, head) \
|
||||
container_of(v, t, head)
|
||||
|
||||
/*
|
||||
* Given the address v of an instance of 'struct dm_list list' contained in
|
||||
* a structure of type t, return the containing structure.
|
||||
*/
|
||||
#define dm_list_item(v, t) dm_list_struct_base((v), t, list)
|
||||
|
||||
/*
|
||||
* Given the address v of one known element e in a known structure of type t,
|
||||
* return another element f.
|
||||
*/
|
||||
#define dm_struct_field(v, t, e, f) \
|
||||
(((t *)((uintptr_t)(v) - offsetof(t, e)))->f)
|
||||
|
||||
/*
|
||||
* Given the address v of a known element e in a known structure of type t,
|
||||
* return the list head 'list'
|
||||
*/
|
||||
#define dm_list_head(v, t, e) dm_struct_field(v, t, e, list)
|
||||
|
||||
/*
|
||||
* Set v to each element of a list in turn.
|
||||
*/
|
||||
#define dm_list_iterate(v, head) \
|
||||
for (v = (head)->n; v != head; v = v->n)
|
||||
|
||||
/*
|
||||
* Set v to each element in a list in turn, starting from the element
|
||||
* in front of 'start'.
|
||||
* You can use this to 'unwind' a list_iterate and back out actions on
|
||||
* already-processed elements.
|
||||
* If 'start' is 'head' it walks the list backwards.
|
||||
*/
|
||||
#define dm_list_uniterate(v, head, start) \
|
||||
for (v = (start)->p; v != head; v = v->p)
|
||||
|
||||
/*
|
||||
* A safe way to walk a list and delete and free some elements along
|
||||
* the way.
|
||||
* t must be defined as a temporary variable of the same type as v.
|
||||
*/
|
||||
#define dm_list_iterate_safe(v, t, head) \
|
||||
for (v = (head)->n, t = v->n; v != head; v = t, t = v->n)
|
||||
|
||||
/*
|
||||
* Walk a list, setting 'v' in turn to the containing structure of each item.
|
||||
* The containing structure should be the same type as 'v'.
|
||||
* The 'struct dm_list' variable within the containing structure is 'field'.
|
||||
*/
|
||||
#define dm_list_iterate_items_gen(v, head, field) \
|
||||
for (v = dm_list_struct_base((head)->n, __typeof__(*v), field); \
|
||||
&v->field != (head); \
|
||||
v = dm_list_struct_base(v->field.n, __typeof__(*v), field))
|
||||
|
||||
/*
|
||||
* Walk a list, setting 'v' in turn to the containing structure of each item.
|
||||
* The containing structure should be the same type as 'v'.
|
||||
* The list should be 'struct dm_list list' within the containing structure.
|
||||
*/
|
||||
#define dm_list_iterate_items(v, head) dm_list_iterate_items_gen(v, (head), list)
|
||||
|
||||
/*
|
||||
* Walk a list, setting 'v' in turn to the containing structure of each item.
|
||||
* The containing structure should be the same type as 'v'.
|
||||
* The 'struct dm_list' variable within the containing structure is 'field'.
|
||||
* t must be defined as a temporary variable of the same type as v.
|
||||
*/
|
||||
#define dm_list_iterate_items_gen_safe(v, t, head, field) \
|
||||
for (v = dm_list_struct_base((head)->n, __typeof__(*v), field), \
|
||||
t = dm_list_struct_base(v->field.n, __typeof__(*v), field); \
|
||||
&v->field != (head); \
|
||||
v = t, t = dm_list_struct_base(v->field.n, __typeof__(*v), field))
|
||||
/*
|
||||
* Walk a list, setting 'v' in turn to the containing structure of each item.
|
||||
* The containing structure should be the same type as 'v'.
|
||||
* The list should be 'struct dm_list list' within the containing structure.
|
||||
* t must be defined as a temporary variable of the same type as v.
|
||||
*/
|
||||
#define dm_list_iterate_items_safe(v, t, head) \
|
||||
dm_list_iterate_items_gen_safe(v, t, (head), list)
|
||||
|
||||
/*
|
||||
* Walk a list backwards, setting 'v' in turn to the containing structure
|
||||
* of each item.
|
||||
* The containing structure should be the same type as 'v'.
|
||||
* The 'struct dm_list' variable within the containing structure is 'field'.
|
||||
*/
|
||||
#define dm_list_iterate_back_items_gen(v, head, field) \
|
||||
for (v = dm_list_struct_base((head)->p, __typeof__(*v), field); \
|
||||
&v->field != (head); \
|
||||
v = dm_list_struct_base(v->field.p, __typeof__(*v), field))
|
||||
|
||||
/*
|
||||
* Walk a list backwards, setting 'v' in turn to the containing structure
|
||||
* of each item.
|
||||
* The containing structure should be the same type as 'v'.
|
||||
* The list should be 'struct dm_list list' within the containing structure.
|
||||
*/
|
||||
#define dm_list_iterate_back_items(v, head) dm_list_iterate_back_items_gen(v, (head), list)
|
||||
|
||||
/*
|
||||
* Return the number of elements in a list by walking it.
|
||||
*/
|
||||
unsigned int dm_list_size(const struct dm_list *head);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
1331
base/data-struct/radix-tree-adaptive.c
Normal file
1331
base/data-struct/radix-tree-adaptive.c
Normal file
File diff suppressed because it is too large
Load Diff
299
base/data-struct/radix-tree-simple.c
Normal file
299
base/data-struct/radix-tree-simple.c
Normal file
@ -0,0 +1,299 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#include "radix-tree.h"
|
||||
|
||||
#include "base/memory/container_of.h"
|
||||
#include "base/memory/zalloc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// This implementation is based around nested binary trees. Very
|
||||
// simple (and hopefully correct).
|
||||
|
||||
struct node {
|
||||
struct node *left;
|
||||
struct node *right;
|
||||
|
||||
uint8_t key;
|
||||
struct node *center;
|
||||
|
||||
bool has_value;
|
||||
union radix_value value;
|
||||
};
|
||||
|
||||
struct radix_tree {
|
||||
radix_value_dtr dtr;
|
||||
void *dtr_context;
|
||||
unsigned nr_entries;
|
||||
|
||||
struct node *root;
|
||||
};
|
||||
|
||||
struct radix_tree *radix_tree_create(radix_value_dtr dtr, void *dtr_context)
|
||||
{
|
||||
struct radix_tree *rt = zalloc(sizeof(*rt));
|
||||
|
||||
if (rt) {
|
||||
rt->dtr = dtr;
|
||||
rt->dtr_context = dtr_context;
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
// Returns the number of entries in the tree
|
||||
static unsigned _destroy_tree(struct node *n, radix_value_dtr dtr, void *context)
|
||||
{
|
||||
unsigned r;
|
||||
|
||||
if (!n)
|
||||
return 0;
|
||||
|
||||
r = _destroy_tree(n->left, dtr, context);
|
||||
r += _destroy_tree(n->right, dtr, context);
|
||||
r += _destroy_tree(n->center, dtr, context);
|
||||
|
||||
if (n->has_value) {
|
||||
if (dtr)
|
||||
dtr(context, n->value);
|
||||
r++;
|
||||
}
|
||||
|
||||
free(n);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void radix_tree_destroy(struct radix_tree *rt)
|
||||
{
|
||||
_destroy_tree(rt->root, rt->dtr, rt->dtr_context);
|
||||
free(rt);
|
||||
}
|
||||
|
||||
static unsigned _count(struct node *n)
|
||||
{
|
||||
unsigned r;
|
||||
|
||||
if (!n)
|
||||
return 0;
|
||||
|
||||
r = _count(n->left);
|
||||
r += _count(n->right);
|
||||
r += _count(n->center);
|
||||
|
||||
if (n->has_value)
|
||||
r++;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned radix_tree_size(struct radix_tree *rt)
|
||||
{
|
||||
return _count(rt->root);
|
||||
}
|
||||
|
||||
static struct node **_lookup(struct node **pn, const uint8_t *kb, const uint8_t *ke)
|
||||
{
|
||||
struct node *n = *pn;
|
||||
|
||||
if (!n || (kb == ke))
|
||||
return pn;
|
||||
|
||||
if (*kb < n->key)
|
||||
return _lookup(&n->left, kb, ke);
|
||||
|
||||
else if (*kb > n->key)
|
||||
return _lookup(&n->right, kb, ke);
|
||||
|
||||
else
|
||||
return _lookup(&n->center, kb + 1, ke);
|
||||
}
|
||||
|
||||
static bool _insert(struct node **pn, const uint8_t *kb, const uint8_t *ke, union radix_value v)
|
||||
{
|
||||
struct node *n = *pn;
|
||||
|
||||
if (!n) {
|
||||
n = zalloc(sizeof(*n));
|
||||
if (!n)
|
||||
return false;
|
||||
|
||||
n->key = *kb;
|
||||
*pn = n;
|
||||
}
|
||||
|
||||
if (kb == ke) {
|
||||
n->has_value = true;
|
||||
n->value = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*kb < n->key)
|
||||
return _insert(&n->left, kb, ke, v);
|
||||
|
||||
else if (*kb > n->key)
|
||||
return _insert(&n->right, kb, ke, v);
|
||||
|
||||
else
|
||||
return _insert(&n->center, kb + 1, ke, v);
|
||||
}
|
||||
|
||||
bool radix_tree_insert(struct radix_tree *rt, const void *key, size_t keylen,
|
||||
union radix_value v)
|
||||
{
|
||||
const uint8_t *kb = key;
|
||||
const uint8_t *ke = kb + keylen;
|
||||
|
||||
if (!_insert(&rt->root, kb, ke, v))
|
||||
return false;
|
||||
|
||||
rt->nr_entries++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool radix_tree_remove(struct radix_tree *rt, const void *key, size_t keylen)
|
||||
{
|
||||
const uint8_t *kb = key;
|
||||
const uint8_t *ke = kb + keylen;
|
||||
struct node **pn = _lookup(&rt->root, kb, ke);
|
||||
struct node *n = *pn;
|
||||
|
||||
if (!n || !n->has_value)
|
||||
return false;
|
||||
|
||||
rt->nr_entries--;
|
||||
|
||||
if (rt->dtr)
|
||||
rt->dtr(rt->dtr_context, n->value);
|
||||
|
||||
if (n->left || n->center || n->right) {
|
||||
n->has_value = false;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// FIXME: delete parent if this was the last entry
|
||||
free(n);
|
||||
*pn = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned radix_tree_remove_prefix(struct radix_tree *rt, const void *prefix, size_t prefix_len)
|
||||
{
|
||||
const uint8_t *kb = prefix;
|
||||
const uint8_t *ke = kb + prefix_len;
|
||||
struct node **pn;
|
||||
unsigned count = 0;
|
||||
|
||||
pn = _lookup(&rt->root, kb, ke);
|
||||
|
||||
if (*pn) {
|
||||
count = _destroy_tree(*pn, rt->dtr, rt->dtr_context);
|
||||
*pn = NULL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool radix_tree_lookup(struct radix_tree *rt, const void *key, size_t keylen,
|
||||
union radix_value *result)
|
||||
{
|
||||
const uint8_t *kb = key;
|
||||
const uint8_t *ke = kb + keylen;
|
||||
struct node **pn = _lookup(&rt->root, kb, ke);
|
||||
struct node *n = *pn;
|
||||
|
||||
if (n && n->has_value) {
|
||||
*result = n->value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _iterate(struct node *n, struct radix_tree_iterator *it)
|
||||
{
|
||||
if (!n)
|
||||
return;
|
||||
|
||||
_iterate(n->left, it);
|
||||
|
||||
if (n->has_value)
|
||||
// FIXME: fill out the key
|
||||
it->visit(it, NULL, 0, n->value);
|
||||
|
||||
_iterate(n->center, it);
|
||||
_iterate(n->right, it);
|
||||
}
|
||||
|
||||
void radix_tree_iterate(struct radix_tree *rt, const void *key, size_t keylen,
|
||||
struct radix_tree_iterator *it)
|
||||
{
|
||||
const uint8_t *kb = key;
|
||||
const uint8_t *ke = kb + keylen;
|
||||
|
||||
if (kb == ke)
|
||||
_iterate(rt->root, it);
|
||||
|
||||
else {
|
||||
struct node **pn = _lookup(&rt->root, kb, ke);
|
||||
struct node *n = *pn;
|
||||
|
||||
if (n) {
|
||||
if (n->has_value)
|
||||
it->visit(it, NULL, 0, n->value);
|
||||
_iterate(n->center, it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool radix_tree_is_well_formed(struct radix_tree *rt)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _dump(FILE *out, struct node *n, unsigned indent)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (!n)
|
||||
return;
|
||||
|
||||
_dump(out, n->left, indent + 1);
|
||||
|
||||
for (i = 0; i < 2 * indent; i++)
|
||||
fprintf(out, " ");
|
||||
|
||||
if (n->has_value) {
|
||||
fprintf(out, "value: %lu\n", (unsigned long) n->value.n);
|
||||
} else {
|
||||
fprintf(out, "key: '%c' [0x%02x] %u\n",
|
||||
isprint(n->key) ? n->key : ' ', n->key, indent);
|
||||
}
|
||||
|
||||
_dump(out, n->center, indent + 1);
|
||||
_dump(out, n->right, indent + 1);
|
||||
}
|
||||
|
||||
void radix_tree_dump(struct radix_tree *rt, FILE *out)
|
||||
{
|
||||
_dump(out, rt->root, 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
63
base/data-struct/radix-tree.c
Normal file
63
base/data-struct/radix-tree.c
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#ifdef SIMPLE_RADIX_TREE
|
||||
#include "base/data-struct/radix-tree-simple.c"
|
||||
#else
|
||||
#include "base/data-struct/radix-tree-adaptive.c"
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct visitor {
|
||||
struct radix_tree_iterator it;
|
||||
unsigned pos, nr_entries;
|
||||
union radix_value *values;
|
||||
};
|
||||
|
||||
static bool _visitor(struct radix_tree_iterator *it,
|
||||
const void *key, size_t keylen,
|
||||
union radix_value v)
|
||||
{
|
||||
struct visitor *vt = container_of(it, struct visitor, it);
|
||||
|
||||
if (vt->pos >= vt->nr_entries)
|
||||
return false;
|
||||
|
||||
vt->values[vt->pos++] = v;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool radix_tree_values(struct radix_tree *rt, const void *key, size_t keylen,
|
||||
union radix_value **values, unsigned *nr_values)
|
||||
{
|
||||
struct visitor vt = {
|
||||
.it.visit = _visitor,
|
||||
.nr_entries = rt->nr_entries,
|
||||
.values = calloc(rt->nr_entries + 1, sizeof(union radix_value)),
|
||||
};
|
||||
|
||||
if (vt.values) {
|
||||
// build set of all values in current radix tree
|
||||
radix_tree_iterate(rt, key, keylen, &vt.it);
|
||||
*nr_values = vt.pos;
|
||||
*values = vt.values;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
93
base/data-struct/radix-tree.h
Normal file
93
base/data-struct/radix-tree.h
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef BASE_DATA_STRUCT_RADIX_TREE_H
|
||||
#define BASE_DATA_STRUCT_RADIX_TREE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct radix_tree;
|
||||
|
||||
union radix_value {
|
||||
void *ptr;
|
||||
uint64_t n;
|
||||
};
|
||||
|
||||
typedef void (*radix_value_dtr)(void *context, union radix_value v);
|
||||
|
||||
// dtr will be called on any deleted entries. dtr may be NULL.
|
||||
struct radix_tree *radix_tree_create(radix_value_dtr dtr, void *dtr_context);
|
||||
void radix_tree_destroy(struct radix_tree *rt);
|
||||
|
||||
unsigned radix_tree_size(struct radix_tree *rt);
|
||||
bool radix_tree_insert(struct radix_tree *rt, const void *key, size_t keylen, union radix_value v);
|
||||
bool radix_tree_remove(struct radix_tree *rt, const void *key, size_t keylen);
|
||||
// Returns: 1 success
|
||||
// 0 failure during insert
|
||||
// -1 key had already existing value (that was updated)
|
||||
int radix_tree_uniq_insert(struct radix_tree *rt, const void *key, size_t keylen, union radix_value v);
|
||||
|
||||
// Returns the number of values removed
|
||||
unsigned radix_tree_remove_prefix(struct radix_tree *rt, const void *prefix, size_t prefix_len);
|
||||
|
||||
bool radix_tree_lookup(struct radix_tree *rt, const void *key, size_t keylen,
|
||||
union radix_value *result);
|
||||
|
||||
// The radix tree stores entries in lexicographical order. Which means
|
||||
// we can iterate entries, in order. Or iterate entries with a particular
|
||||
// prefix.
|
||||
struct radix_tree_iterator {
|
||||
// Returns false if the iteration should end.
|
||||
bool (*visit)(struct radix_tree_iterator *it,
|
||||
const void *key, size_t keylen, union radix_value v);
|
||||
};
|
||||
|
||||
void radix_tree_iterate(struct radix_tree *rt, const void *key, size_t keylen,
|
||||
struct radix_tree_iterator *it);
|
||||
|
||||
// Alternative traversing radix_tree.
|
||||
// Builds whole set all radix_tree nr_values values.
|
||||
// After use, free(values).
|
||||
bool radix_tree_values(struct radix_tree *rt, const void *key, size_t keylen,
|
||||
union radix_value **values, unsigned *nr_values);
|
||||
|
||||
// Checks that some constraints on the shape of the tree are
|
||||
// being held. For debug only.
|
||||
bool radix_tree_is_well_formed(struct radix_tree *rt);
|
||||
void radix_tree_dump(struct radix_tree *rt, FILE *out);
|
||||
|
||||
// Shortcut for ptr value return
|
||||
// Note: if value would be NULL, it's same result for not/found case.
|
||||
static inline void *radix_tree_lookup_ptr(struct radix_tree *rt, const void *key, size_t keylen)
|
||||
{
|
||||
union radix_value v;
|
||||
return radix_tree_lookup(rt, key, keylen, &v) ? v.ptr : NULL;
|
||||
}
|
||||
|
||||
static inline bool radix_tree_insert_ptr(struct radix_tree *rt, const void *key, size_t keylen, void *ptr)
|
||||
{
|
||||
union radix_value v = { .ptr = ptr };
|
||||
return radix_tree_insert(rt, key, keylen, v);
|
||||
}
|
||||
|
||||
static inline int radix_tree_uniq_insert_ptr(struct radix_tree *rt, const void *key, size_t keylen, void *ptr)
|
||||
{
|
||||
union radix_value v = { .ptr = ptr };
|
||||
return radix_tree_uniq_insert(rt, key, keylen, v);
|
||||
}
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
25
base/memory/container_of.h
Normal file
25
base/memory/container_of.h
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2018 - 2020 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef BASE_MEMORY_CONTAINER_OF_H
|
||||
#define BASE_MEMORY_CONTAINER_OF_H
|
||||
|
||||
#include <stddef.h> // offsetof
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#define container_of(v, t, head) \
|
||||
((t *)((char *)(v) - offsetof(t, head)))
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
27
base/memory/zalloc.h
Normal file
27
base/memory/zalloc.h
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef BASE_MEMORY_ZALLOC_H
|
||||
#define BASE_MEMORY_ZALLOC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static inline void *zalloc(size_t len)
|
||||
{
|
||||
return calloc(1, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
6
conf/.gitignore
vendored
Normal file
6
conf/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
command_profile_template.profile
|
||||
example.conf
|
||||
lvmlocal.conf
|
||||
metadata_profile_template.profile
|
||||
configure.h
|
||||
lvm-version.h
|
60
conf/Makefile.in
Normal file
60
conf/Makefile.in
Normal file
@ -0,0 +1,60 @@
|
||||
#
|
||||
# Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
CONFSRC=example.conf
|
||||
CONFDEST=lvm.conf
|
||||
CONFLOCAL=lvmlocal.conf
|
||||
|
||||
PROFILE_TEMPLATES=command_profile_template.profile metadata_profile_template.profile
|
||||
PROFILES=$(PROFILE_TEMPLATES) \
|
||||
$(srcdir)/cache-mq.profile \
|
||||
$(srcdir)/cache-smq.profile \
|
||||
$(srcdir)/thin-generic.profile \
|
||||
$(srcdir)/thin-performance.profile \
|
||||
$(srcdir)/vdo-small.profile \
|
||||
$(srcdir)/lvmdbusd.profile
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
.PHONY: install_conf install_localconf install_profiles
|
||||
|
||||
generate:
|
||||
$(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withgeneralpreamble --withcomments --ignorelocal --withspaces > example.conf.in
|
||||
$(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withlocalpreamble --withcomments --withspaces local > lvmlocal.conf.in
|
||||
|
||||
install_conf: $(CONFSRC)
|
||||
@if [ ! -e $(confdir)/$(CONFDEST) ]; then \
|
||||
echo "$(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST)"; \
|
||||
$(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST); \
|
||||
fi
|
||||
|
||||
install_localconf: $(CONFLOCAL)
|
||||
@if [ ! -e $(confdir)/$(CONFLOCAL) ]; then \
|
||||
echo "$(INSTALL_WDATA) -D $< $(confdir)/$(CONFLOCAL)"; \
|
||||
$(INSTALL_WDATA) -D $< $(confdir)/$(CONFLOCAL); \
|
||||
fi
|
||||
|
||||
install_profiles: $(PROFILES)
|
||||
$(SHOW) " [INSTALL] $<"
|
||||
$(Q) $(INSTALL_DIR) $(profiledir)
|
||||
$(Q) $(INSTALL_DATA) $(PROFILES) $(profiledir)/
|
||||
|
||||
install_lvm2: install_conf install_localconf install_profiles
|
||||
|
||||
install: install_lvm2
|
||||
|
||||
DISTCLEAN_TARGETS += $(CONFSRC) $(CONFLOCAL) $(PROFILE_TEMPLATES)
|
20
conf/cache-mq.profile
Normal file
20
conf/cache-mq.profile
Normal file
@ -0,0 +1,20 @@
|
||||
# Demo configuration 'mq' cache policy
|
||||
#
|
||||
# Note: This policy has been deprecated in favor of the smq policy
|
||||
# keyword "default" means, setting is left with kernel defaults.
|
||||
#
|
||||
|
||||
allocation {
|
||||
cache_pool_chunk_size = 64
|
||||
cache_mode = "writethrough"
|
||||
cache_policy = "mq"
|
||||
cache_settings {
|
||||
mq {
|
||||
sequential_threshold = "default" # #nr_sequential_ios
|
||||
random_threshold = "default" # #nr_random_ios
|
||||
read_promote_adjustment = "default"
|
||||
write_promote_adjustment = "default"
|
||||
discard_promote_adjustment = "default"
|
||||
}
|
||||
}
|
||||
}
|
14
conf/cache-smq.profile
Normal file
14
conf/cache-smq.profile
Normal file
@ -0,0 +1,14 @@
|
||||
# Demo configuration 'smq' cache policy
|
||||
#
|
||||
# The stochastic multi-queue (smq) policy addresses some of the problems
|
||||
# with the multiqueue (mq) policy and uses less memory.
|
||||
#
|
||||
|
||||
allocation {
|
||||
cache_pool_chunk_size = 64
|
||||
cache_mode = "writethrough"
|
||||
cache_policy = "smq"
|
||||
cache_settings {
|
||||
# currently no settings for "smq" policy
|
||||
}
|
||||
}
|
74
conf/command_profile_template.profile.in
Normal file
74
conf/command_profile_template.profile.in
Normal file
@ -0,0 +1,74 @@
|
||||
# This is a command profile template for the LVM2 system.
|
||||
#
|
||||
# It contains all configuration settings that are customizable by command
|
||||
# profiles. To create a new command profile, select the settings you want
|
||||
# to customize and add them in a new file named <profile_name>.profile.
|
||||
# Then install the new profile in a directory as defined by config/profile_dir
|
||||
# setting found in @DEFAULT_SYS_DIR@/lvm.conf file.
|
||||
#
|
||||
# Command profiles can be referenced by using the --commandprofile option then.
|
||||
#
|
||||
# Refer to 'man lvm.conf' for further information about profiles and
|
||||
# general configuration file layout.
|
||||
#
|
||||
allocation {
|
||||
cache_mode="writethrough"
|
||||
cache_settings {
|
||||
}
|
||||
}
|
||||
log {
|
||||
report_command_log=0
|
||||
command_log_sort="log_seq_num"
|
||||
command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
||||
command_log_selection="!(log_type=status && message=success)"
|
||||
}
|
||||
global {
|
||||
units="h"
|
||||
si_unit_consistency=1
|
||||
suffix=1
|
||||
lvdisplay_shows_full_device_path=0
|
||||
}
|
||||
report {
|
||||
output_format="basic"
|
||||
compact_output=0
|
||||
compact_output_cols=""
|
||||
aligned=1
|
||||
buffered=1
|
||||
headings=1
|
||||
separator=" "
|
||||
list_item_separator=","
|
||||
prefixes=0
|
||||
quoted=1
|
||||
columns_as_rows=0
|
||||
binary_values_as_numeric=0
|
||||
time_format="%Y-%m-%d %T %z"
|
||||
devtypes_sort="devtype_name"
|
||||
devtypes_cols="devtype_name,devtype_max_partitions,devtype_description"
|
||||
devtypes_cols_verbose="devtype_name,devtype_max_partitions,devtype_description"
|
||||
lvs_sort="vg_name,lv_name"
|
||||
lvs_cols="lv_name,vg_name,lv_attr,lv_size,pool_lv,origin,data_percent,metadata_percent,move_pv,mirror_log,copy_percent,convert_lv"
|
||||
lvs_cols_verbose="lv_name,vg_name,seg_count,lv_attr,lv_size,lv_major,lv_minor,lv_kernel_major,lv_kernel_minor,pool_lv,origin,data_percent,metadata_percent,move_pv,copy_percent,mirror_log,convert_lv,lv_uuid,lv_profile"
|
||||
vgs_sort="vg_name"
|
||||
vgs_cols="vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free"
|
||||
vgs_cols_verbose="vg_name,vg_attr,vg_extent_size,pv_count,lv_count,snap_count,vg_size,vg_free,vg_uuid,vg_profile"
|
||||
pvs_sort="pv_name"
|
||||
pvs_cols="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free"
|
||||
pvs_cols_verbose="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,dev_size,pv_uuid"
|
||||
segs_sort="vg_name,lv_name,seg_start"
|
||||
segs_cols="lv_name,vg_name,lv_attr,stripes,segtype,seg_size"
|
||||
segs_cols_verbose="lv_name,vg_name,lv_attr,seg_start,seg_size,stripes,segtype,stripesize,chunksize"
|
||||
pvsegs_sort="pv_name,pvseg_start"
|
||||
pvsegs_cols="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
|
||||
pvsegs_cols_verbose="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
||||
vgs_cols_full="vg_all"
|
||||
pvs_cols_full="pv_all"
|
||||
lvs_cols_full="lv_all"
|
||||
pvsegs_cols_full="pvseg_all,pv_uuid,lv_uuid"
|
||||
segs_cols_full="seg_all,lv_uuid"
|
||||
vgs_sort_full="vg_name"
|
||||
pvs_sort_full="pv_name"
|
||||
lvs_sort_full="vg_name,lv_name"
|
||||
pvsegs_sort_full="pv_uuid,pvseg_start"
|
||||
segs_sort_full="lv_uuid,seg_start"
|
||||
mark_hidden_devices=1
|
||||
}
|
2514
conf/example.conf.in
Normal file
2514
conf/example.conf.in
Normal file
File diff suppressed because it is too large
Load Diff
50
conf/lvmdbusd.profile
Normal file
50
conf/lvmdbusd.profile
Normal file
@ -0,0 +1,50 @@
|
||||
#
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# LVM configuration profile used by lvmdbusd daemon.
|
||||
#
|
||||
# This sets up LVM to produce output in the most suitable format for processing
|
||||
# by lvmdbusd daemon which utilizes LVM shell to execute LVM commands.
|
||||
#
|
||||
# Do not edit this file in any way. This profile is distributed together with
|
||||
# lvmdbusd and it contains configuration that is important for lvmdbusd to
|
||||
# cooperate and interface with LVM correctly.
|
||||
#
|
||||
|
||||
global {
|
||||
# use bytes for expected and deterministic output
|
||||
units=b
|
||||
# no need for suffix if we have units set
|
||||
suffix=0
|
||||
}
|
||||
|
||||
report {
|
||||
compact_output=0
|
||||
compact_output_cols=""
|
||||
binary_values_as_numeric=0
|
||||
# time in number of seconds since the Epoch
|
||||
time_format="%s"
|
||||
mark_hidden_devices=1
|
||||
# lvmdbusd expects JSON output
|
||||
output_format=json
|
||||
# *_cols_full for lvm fullreport's fields which lvmdbusd relies on to update its state
|
||||
vgs_cols_full="vg_name,vg_uuid,vg_fmt,vg_size,vg_free,vg_sysid,vg_extent_size,vg_extent_count,vg_free_count,vg_profile,max_lv,max_pv,pv_count,lv_count,snap_count,vg_seqno,vg_mda_count,vg_mda_free,vg_mda_size,vg_mda_used_count,vg_attr,vg_tags"
|
||||
pvs_cols_full="pv_name,pv_uuid,pv_fmt,pv_size,pv_free,pv_used,dev_size,pv_mda_size,pv_mda_free,pv_ba_start,pv_ba_size,pe_start,pv_pe_count,pv_pe_alloc_count,pv_attr,pv_tags,vg_name,vg_uuid"
|
||||
lvs_cols_full="lv_uuid,lv_name,lv_path,lv_size,vg_name,pool_lv_uuid,pool_lv,origin_uuid,origin,data_percent,lv_attr,lv_tags,vg_uuid,lv_active,data_lv,metadata_lv,lv_parent,lv_role,lv_layout"
|
||||
pvsegs_cols_full="pvseg_start,pvseg_size,segtype,pv_uuid,lv_uuid,pv_name"
|
||||
segs_cols_full="seg_pe_ranges,segtype,lv_uuid"
|
||||
vgs_sort_full="vg_name"
|
||||
pvs_sort_full="pv_name"
|
||||
lvs_sort_full="vg_name,lv_name"
|
||||
pvsegs_sort_full="pv_uuid,pvseg_start"
|
||||
segs_sort_full="lv_uuid,seg_start"
|
||||
}
|
||||
|
||||
log {
|
||||
# lvmdbusd relies on command log report to inspect LVM command's execution status
|
||||
report_command_log=1
|
||||
# display only outermost LVM shell-related log that lvmdbusd inspects first after LVM command execution (it calls 'lastlog' for more detailed log afterwards if needed)
|
||||
command_log_selection="log_context=shell"
|
||||
command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
||||
command_log_sort="log_seq_num"
|
||||
}
|
58
conf/lvmlocal.conf.in
Normal file
58
conf/lvmlocal.conf.in
Normal file
@ -0,0 +1,58 @@
|
||||
# This is a local configuration file template for the LVM2 system
|
||||
# which should be installed as @DEFAULT_SYS_DIR@/lvmlocal.conf .
|
||||
#
|
||||
# Refer to 'man lvm.conf' for information about the file layout.
|
||||
#
|
||||
# To put this file in a different directory and override
|
||||
# @DEFAULT_SYS_DIR@ set the environment variable LVM_SYSTEM_DIR before
|
||||
# running the tools.
|
||||
#
|
||||
# The lvmlocal.conf file is normally expected to contain only the
|
||||
# "local" section which contains settings that should not be shared or
|
||||
# repeated among different hosts. (But if other sections are present,
|
||||
# they *will* get processed. Settings in this file override equivalent
|
||||
# ones in lvm.conf and are in turn overridden by ones in any enabled
|
||||
# lvm_<tag>.conf files.)
|
||||
#
|
||||
# Please take care that each setting only appears once if uncommenting
|
||||
# example settings in this file and never copy this file between hosts.
|
||||
|
||||
|
||||
# Configuration section local.
|
||||
# LVM settings that are specific to the local host.
|
||||
local {
|
||||
|
||||
# Configuration option local/system_id.
|
||||
# Defines the local system ID for lvmlocal mode.
|
||||
# This is used when global/system_id_source is set to 'lvmlocal' in the
|
||||
# main configuration file, e.g. lvm.conf. When used, it must be set to
|
||||
# a unique value among all hosts sharing access to the storage,
|
||||
# e.g. a host name.
|
||||
#
|
||||
# Example
|
||||
# Set no system ID:
|
||||
# system_id = ""
|
||||
# Set the system_id to a specific name:
|
||||
# system_id = "host1"
|
||||
#
|
||||
# This configuration option has an automatic default value.
|
||||
# system_id = ""
|
||||
|
||||
# Configuration option local/extra_system_ids.
|
||||
# A list of extra VG system IDs the local host can access.
|
||||
# VGs with the system IDs listed here (in addition to the host's own
|
||||
# system ID) can be fully accessed by the local host. (These are
|
||||
# system IDs that the host sees in VGs, not system IDs that identify
|
||||
# the local host, which is determined by system_id_source.)
|
||||
# Use this only after consulting 'man lvmsystemid' to be certain of
|
||||
# correct usage and possible dangers.
|
||||
# This configuration option does not have a default value defined.
|
||||
|
||||
# Configuration option local/host_id.
|
||||
# The sanlock host_id used by lvmlockd. This must be unique among all the hosts
|
||||
# using shared VGs with sanlock. Accepted values are 1-2000, except when sanlock_align_size
|
||||
# is configured to 1, 2 or 4, which correspond to max host_id values of 250, 500, or 1000.
|
||||
# Applicable only if LVM is compiled with support for lvmlockd+sanlock.
|
||||
# This configuration option has an automatic default value.
|
||||
# host_id = 0
|
||||
}
|
24
conf/metadata_profile_template.profile.in
Normal file
24
conf/metadata_profile_template.profile.in
Normal file
@ -0,0 +1,24 @@
|
||||
# This is a metadata profile template for the LVM2 system.
|
||||
#
|
||||
# It contains all configuration settings that are customizable by metadata
|
||||
# profiles. To create a new metadata profile, select the settings you want
|
||||
# to customize and add them in a new file named <profile_name>.profile.
|
||||
# Then install the new profile in a directory as defined by config/profile_dir
|
||||
# setting found in @DEFAULT_SYS_DIR@/lvm.conf file.
|
||||
#
|
||||
# Metadata profiles can be referenced by using the --metadataprofile LVM2
|
||||
# command line option.
|
||||
#
|
||||
# Refer to 'man lvm.conf' for further information about profiles and
|
||||
# general configuration file layout.
|
||||
#
|
||||
allocation {
|
||||
thin_pool_zero=1
|
||||
thin_pool_discards="passdown"
|
||||
thin_pool_chunk_size_policy="generic"
|
||||
# thin_pool_chunk_size=128
|
||||
}
|
||||
activation {
|
||||
thin_pool_autoextend_threshold=100
|
||||
thin_pool_autoextend_percent=20
|
||||
}
|
4
conf/thin-generic.profile
Normal file
4
conf/thin-generic.profile
Normal file
@ -0,0 +1,4 @@
|
||||
allocation {
|
||||
thin_pool_chunk_size_policy = "generic"
|
||||
thin_pool_zero = 1
|
||||
}
|
4
conf/thin-performance.profile
Normal file
4
conf/thin-performance.profile
Normal file
@ -0,0 +1,4 @@
|
||||
allocation {
|
||||
thin_pool_chunk_size_policy = "performance"
|
||||
thin_pool_zero = 0
|
||||
}
|
21
conf/vdo-small.profile
Normal file
21
conf/vdo-small.profile
Normal file
@ -0,0 +1,21 @@
|
||||
# Demo configuration for 'VDO' using less memory.
|
||||
# ~lvmconfig --type full | grep vdo
|
||||
|
||||
allocation {
|
||||
vdo_use_compression=1
|
||||
vdo_use_deduplication=1
|
||||
vdo_minimum_io_size=4096
|
||||
vdo_block_map_cache_size_mb=128
|
||||
vdo_block_map_period=16380
|
||||
vdo_use_sparse_index=0
|
||||
vdo_index_memory_size_mb=256
|
||||
vdo_slab_size_mb=2048
|
||||
vdo_ack_threads=1
|
||||
vdo_bio_threads=1
|
||||
vdo_bio_rotation=64
|
||||
vdo_cpu_threads=2
|
||||
vdo_hash_zone_threads=1
|
||||
vdo_logical_threads=1
|
||||
vdo_physical_threads=1
|
||||
vdo_max_discard=1
|
||||
}
|
2134
configure.ac
Normal file
2134
configure.ac
Normal file
File diff suppressed because it is too large
Load Diff
155
coverity/coverity_model.c
Normal file
155
coverity/coverity_model.c
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Coverity usage:
|
||||
*
|
||||
* translate model into xml
|
||||
* cov-make-library -of coverity_model.xml coverity_model.c
|
||||
*
|
||||
* compile (using outdir 'cov'):
|
||||
* cov-build --dir=cov make CC=gcc
|
||||
*
|
||||
* analyze (aggressively, using 'cov')
|
||||
* cov-analyze --dir cov --wait-for-license --hfa --concurrency --enable-fnptr --enable-constraint-fpp --security --all --aggressiveness-level=high --field-offset-escape --user-model-file=coverity/coverity_model.xml
|
||||
*
|
||||
* generate html output (to 'html' from 'cov'):
|
||||
* cov-format-errors --dir cov --html-output html
|
||||
*/
|
||||
|
||||
struct lv_segment;
|
||||
struct logical_volume;
|
||||
struct cmd_context;
|
||||
struct profile;
|
||||
|
||||
struct lv_segment *first_seg(const struct logical_volume *lv)
|
||||
{
|
||||
return ((struct lv_segment **)lv)[0];
|
||||
}
|
||||
|
||||
struct lv_segment *last_seg(const struct logical_volume *lv)
|
||||
{
|
||||
return ((struct lv_segment **)lv)[0];
|
||||
}
|
||||
|
||||
const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile)
|
||||
{
|
||||
return "STRING";
|
||||
}
|
||||
|
||||
/*
|
||||
struct logical_volume *origin_from_cow(const struct logical_volume *lv)
|
||||
{
|
||||
if (lv)
|
||||
return lv;
|
||||
|
||||
__coverity_panic__();
|
||||
}
|
||||
*/
|
||||
|
||||
/* simple_memccpy() from glibc */
|
||||
void *memccpy(void *dest, const void *src, int c, unsigned long n)
|
||||
{
|
||||
const char *s = src;
|
||||
char *d = dest;
|
||||
|
||||
while (n-- > 0)
|
||||
if ((*d++ = *s++) == (char) c)
|
||||
return d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2 lines below needs to be placed in coverity/config/user_nodefs.h
|
||||
* Not sure about any other way.
|
||||
* Without them, coverity shows warning since x86 system header files
|
||||
* are using inline assembly to reset fdset
|
||||
*/
|
||||
//#nodef FD_ZERO model_FD_ZERO
|
||||
//void model_FD_ZERO(void *fdset);
|
||||
|
||||
void model_FD_ZERO(void *fdset)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < 1024 / 8 / sizeof(long); ++i)
|
||||
((long*)fdset)[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Resent Coverity reports quite weird errors... */
|
||||
int *__errno_location(void)
|
||||
{
|
||||
static int _i = 0;
|
||||
return &_i;
|
||||
}
|
||||
|
||||
const unsigned short **__ctype_b_loc (void)
|
||||
{
|
||||
static const unsigned short *_a[1] = { 0 };
|
||||
return _a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Added extra pointer check to not need these models,
|
||||
* for now just keep then in file
|
||||
*/
|
||||
|
||||
/*
|
||||
struct cmd_context;
|
||||
struct profile;
|
||||
|
||||
const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile)
|
||||
{
|
||||
return "text";
|
||||
}
|
||||
|
||||
const char *find_config_tree_str_allow_empty(struct cmd_context *cmd, int id, struct profile *profile)
|
||||
{
|
||||
return "text";
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* Until fixed coverity case# 00531860:
|
||||
* A FORWARD_NULL false positive on a recursive function call
|
||||
*
|
||||
* model also these functions:
|
||||
*/
|
||||
/*
|
||||
const struct dm_config_node;
|
||||
const struct dm_config_node *find_config_tree_array(struct cmd_context *cmd, int id, struct profile *profile)
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
const struct dm_config_node *find_config_tree_node(struct cmd_context *cmd, int id, struct profile *profile)
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
int find_config_tree_bool(struct cmd_context *cmd, int id, struct profile *profile)
|
||||
{
|
||||
int b;
|
||||
|
||||
return b;
|
||||
}
|
||||
*/
|
51
daemons/Makefile.in
Normal file
51
daemons/Makefile.in
Normal file
@ -0,0 +1,51 @@
|
||||
#
|
||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
.PHONY: dmeventd cmirrord lvmpolld lvmlockd
|
||||
|
||||
ifeq ("@BUILD_CMIRRORD@", "yes")
|
||||
SUBDIRS += cmirrord
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_DMEVENTD@", "yes")
|
||||
SUBDIRS += dmeventd
|
||||
ifneq ("$(CFLOW_CMD)", "")
|
||||
daemons.cflow: dmeventd.cflow
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LVMPOLLD@", "yes")
|
||||
SUBDIRS += lvmpolld
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LVMLOCKD@", "yes")
|
||||
SUBDIRS += lvmlockd
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LVMDBUSD@", "yes")
|
||||
SUBDIRS += lvmdbusd
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = cmirrord dmeventd lvmpolld lvmlockd lvmdbusd
|
||||
endif
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
ifeq ("@BUILD_DMEVENTD@", "yes")
|
||||
device-mapper: dmeventd.device-mapper
|
||||
endif
|
1
daemons/cmirrord/.gitignore
vendored
Normal file
1
daemons/cmirrord/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
cmirrord
|
43
daemons/cmirrord/Makefile.in
Normal file
43
daemons/cmirrord/Makefile.in
Normal file
@ -0,0 +1,43 @@
|
||||
#
|
||||
# Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
CPG_LIBS = @CPG_LIBS@
|
||||
CPG_CFLAGS = @CPG_CFLAGS@
|
||||
|
||||
SOURCES = clogd.c cluster.c compat.c functions.c link_mon.c local.c logging.c
|
||||
|
||||
TARGETS = cmirrord
|
||||
|
||||
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
|
||||
CFLOW_TARGET := $(TARGETS)
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
LMLIBS += $(CPG_LIBS)
|
||||
CFLAGS += $(CPG_CFLAGS) $(EXTRA_EXEC_CFLAGS)
|
||||
LDFLAGS += $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS)
|
||||
|
||||
cmirrord: $(OBJECTS)
|
||||
$(SHOW) " [CC] $@"
|
||||
$(Q) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
|
||||
$(LMLIBS) -L$(top_builddir)/libdm -ldevmapper $(LIBS)
|
||||
|
||||
install_cluster: $(TARGETS)
|
||||
$(SHOW) " [INSTALL] $<"
|
||||
$(Q) $(INSTALL_PROGRAM) -D $< $(usrsbindir)/$(<F)
|
||||
|
||||
install: install_cluster
|
293
daemons/cmirrord/clogd.c
Normal file
293
daemons/cmirrord/clogd.c
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "logging.h"
|
||||
#include "common.h"
|
||||
#include "functions.h"
|
||||
#include "link_mon.h"
|
||||
#include "local.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static volatile sig_atomic_t exit_now = 0;
|
||||
/* FIXME Review signal handling. Should be volatile sig_atomic_t */
|
||||
static sigset_t signal_mask;
|
||||
static volatile sig_atomic_t signal_received;
|
||||
|
||||
static void process_signals(void);
|
||||
static void daemonize(void);
|
||||
static void init_all(void);
|
||||
static void cleanup_all(void);
|
||||
|
||||
static void usage (FILE *dest)
|
||||
{
|
||||
fprintf (dest, "Usage: cmirrord [options]\n"
|
||||
" -f, --foreground stay in the foreground, log to the terminal\n"
|
||||
" -h, --help print this help\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int foreground_mode = 0;
|
||||
static const struct option _long_options[] = {
|
||||
{ "foreground", no_argument, NULL, 'f' },
|
||||
{ "help" , no_argument, NULL, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt_long (argc, argv, "fh", _long_options, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
foreground_mode = 1;
|
||||
break;
|
||||
case 'h':
|
||||
usage (stdout);
|
||||
exit (0);
|
||||
default:
|
||||
usage (stderr);
|
||||
exit (2);
|
||||
}
|
||||
}
|
||||
if (optind < argc) {
|
||||
usage (stderr);
|
||||
exit (2);
|
||||
}
|
||||
|
||||
if (!foreground_mode)
|
||||
daemonize();
|
||||
|
||||
init_all();
|
||||
|
||||
/* Parent can now exit, we're ready to handle requests */
|
||||
if (!foreground_mode)
|
||||
kill(getppid(), SIGTERM);
|
||||
|
||||
LOG_PRINT("Starting cmirrord:");
|
||||
LOG_PRINT(" Built: "__DATE__" "__TIME__"\n");
|
||||
LOG_DBG(" Compiled with debugging.");
|
||||
|
||||
while (!exit_now) {
|
||||
links_monitor();
|
||||
|
||||
links_issue_callbacks();
|
||||
|
||||
process_signals();
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* parent_exit_handler: exit the parent
|
||||
* @sig: the signal
|
||||
*
|
||||
*/
|
||||
static void parent_exit_handler(int sig __attribute__((unused)))
|
||||
{
|
||||
exit_now = 1;
|
||||
}
|
||||
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
/* FIXME Races - don't touch signal_mask here. */
|
||||
sigaddset(&signal_mask, sig);
|
||||
signal_received = 1;
|
||||
}
|
||||
|
||||
static void process_signal(int sig){
|
||||
int r = 0;
|
||||
|
||||
switch(sig) {
|
||||
case SIGINT:
|
||||
case SIGQUIT:
|
||||
case SIGTERM:
|
||||
case SIGHUP:
|
||||
r += log_status();
|
||||
break;
|
||||
case SIGUSR1:
|
||||
case SIGUSR2:
|
||||
log_debug();
|
||||
/*local_debug();*/
|
||||
cluster_debug();
|
||||
return;
|
||||
default:
|
||||
LOG_PRINT("Unknown signal received... ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
LOG_DBG("No current cluster logs... safe to exit.");
|
||||
cleanup_all();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
LOG_ERROR("Cluster logs exist. Refusing to exit.");
|
||||
}
|
||||
|
||||
static void process_signals(void)
|
||||
{
|
||||
int x;
|
||||
|
||||
if (!signal_received)
|
||||
return;
|
||||
|
||||
signal_received = 0;
|
||||
|
||||
for (x = 1; x < _NSIG; x++) {
|
||||
if (sigismember(&signal_mask, x)) {
|
||||
sigdelset(&signal_mask, x);
|
||||
process_signal(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_lockfile(void)
|
||||
{
|
||||
if (unlink(CMIRRORD_PIDFILE))
|
||||
LOG_ERROR("Unable to remove \"" CMIRRORD_PIDFILE "\" %s", strerror(errno));
|
||||
}
|
||||
|
||||
/*
|
||||
* daemonize
|
||||
*
|
||||
* Performs the steps necessary to become a daemon.
|
||||
*/
|
||||
static void daemonize(void)
|
||||
{
|
||||
int pid;
|
||||
int status;
|
||||
int devnull;
|
||||
|
||||
if ((devnull = open("/dev/null", O_RDWR)) == -1) {
|
||||
LOG_ERROR("Can't open /dev/null: %s", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
signal(SIGTERM, &parent_exit_handler);
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid < 0) {
|
||||
LOG_ERROR("Unable to fork()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pid) {
|
||||
/* Parent waits here for child to get going */
|
||||
while (!waitpid(pid, &status, WNOHANG) && !exit_now);
|
||||
if (exit_now)
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
switch (WEXITSTATUS(status)) {
|
||||
case EXIT_LOCKFILE:
|
||||
LOG_ERROR("Failed to create lockfile");
|
||||
LOG_ERROR("Process already running?");
|
||||
break;
|
||||
case EXIT_KERNEL_SOCKET:
|
||||
LOG_ERROR("Unable to create netlink socket");
|
||||
break;
|
||||
case EXIT_KERNEL_BIND:
|
||||
LOG_ERROR("Unable to bind to netlink socket");
|
||||
break;
|
||||
case EXIT_KERNEL_SETSOCKOPT:
|
||||
LOG_ERROR("Unable to setsockopt on netlink socket");
|
||||
break;
|
||||
case EXIT_CLUSTER_CKPT_INIT:
|
||||
LOG_ERROR("Unable to initialize checkpoint service");
|
||||
LOG_ERROR("Has the cluster infrastructure been started?");
|
||||
break;
|
||||
case EXIT_FAILURE:
|
||||
LOG_ERROR("Failed to start: Generic error");
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Failed to start: Unknown error");
|
||||
break;
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
setsid();
|
||||
if (chdir("/")) {
|
||||
LOG_ERROR("Failed to chdir /: %s", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
umask(0);
|
||||
|
||||
if (close(0) || close(1) || close(2)) {
|
||||
LOG_ERROR("Failed to close terminal FDs");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if ((dup2(devnull, 0) < 0) || /* reopen stdin */
|
||||
(dup2(devnull, 1) < 0) || /* reopen stdout */
|
||||
(dup2(devnull, 2) < 0)) /* reopen stderr */
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if ((devnull > STDERR_FILENO) && close(devnull)) {
|
||||
LOG_ERROR("Failed to close descriptor %d: %s",
|
||||
devnull, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LOG_OPEN("cmirrord", LOG_PID, LOG_DAEMON);
|
||||
/* coverity[leaked_handle] devnull cannot leak here */
|
||||
}
|
||||
|
||||
/*
|
||||
* init_all
|
||||
*
|
||||
* Initialize modules. Exit on failure.
|
||||
*/
|
||||
static void init_all(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
(void) dm_prepare_selinux_context(CMIRRORD_PIDFILE, S_IFREG);
|
||||
if (dm_create_lockfile(CMIRRORD_PIDFILE) == 0)
|
||||
exit(EXIT_LOCKFILE);
|
||||
(void) dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
atexit(remove_lockfile);
|
||||
|
||||
/* FIXME Replace with sigaction. (deprecated) */
|
||||
signal(SIGINT, &sig_handler);
|
||||
signal(SIGQUIT, &sig_handler);
|
||||
signal(SIGTERM, &sig_handler);
|
||||
signal(SIGHUP, &sig_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGUSR1, &sig_handler);
|
||||
signal(SIGUSR2, &sig_handler);
|
||||
sigemptyset(&signal_mask);
|
||||
signal_received = 0;
|
||||
|
||||
if ((r = init_local()) ||
|
||||
(r = init_cluster())) {
|
||||
exit(r);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* cleanup_all
|
||||
*
|
||||
* Clean up before exiting
|
||||
*/
|
||||
static void cleanup_all(void)
|
||||
{
|
||||
cleanup_local();
|
||||
cleanup_cluster();
|
||||
}
|
1818
daemons/cmirrord/cluster.c
Normal file
1818
daemons/cmirrord/cluster.c
Normal file
File diff suppressed because it is too large
Load Diff
76
daemons/cmirrord/cluster.h
Normal file
76
daemons/cmirrord/cluster.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef _LVM_CLOG_CLUSTER_H
|
||||
#define _LVM_CLOG_CLUSTER_H
|
||||
|
||||
#include "libdm/libdevmapper.h"
|
||||
#include "libdm/misc/dm-log-userspace.h"
|
||||
|
||||
#define DM_ULOG_RESPONSE 0x1000U /* in last byte of 32-bit value */
|
||||
#define DM_ULOG_CHECKPOINT_READY 21
|
||||
#define DM_ULOG_MEMBER_JOIN 22
|
||||
|
||||
/*
|
||||
* There is other information in addition to what can
|
||||
* be found in the dm_ulog_request structure that we
|
||||
* need for processing. 'clog_request' is the wrapping
|
||||
* structure we use to make the additional fields
|
||||
* available.
|
||||
*/
|
||||
struct clog_request {
|
||||
/*
|
||||
* If we don't use a union, the structure size will
|
||||
* vary between 32-bit and 64-bit machines. So, we
|
||||
* pack two 64-bit version numbers in there to force
|
||||
* the size of the structure to be the same.
|
||||
*
|
||||
* The two version numbers also help us with endian
|
||||
* issues. The first is always little endian, while
|
||||
* the second is in native format of the sending
|
||||
* machine. If the two are equal, there is no need
|
||||
* to do endian conversions.
|
||||
*/
|
||||
union version_u {
|
||||
uint64_t version[2]; /* LE version and native version */
|
||||
struct dm_list list;
|
||||
} u;
|
||||
|
||||
/*
|
||||
* 'originator' is the machine from which the requests
|
||||
* was made.
|
||||
*/
|
||||
uint32_t originator;
|
||||
|
||||
/*
|
||||
* 'pit_server' is the "point-in-time" server for the
|
||||
* request. (I.e. The machine that was the server at
|
||||
* the time the request was issued - only important during
|
||||
* startup.
|
||||
*/
|
||||
uint32_t pit_server;
|
||||
|
||||
/*
|
||||
* The request from the kernel that is being processed
|
||||
*/
|
||||
struct dm_ulog_request u_rq;
|
||||
};
|
||||
|
||||
int init_cluster(void);
|
||||
void cleanup_cluster(void);
|
||||
void cluster_debug(void);
|
||||
|
||||
int create_cluster_cpg(char *uuid, uint64_t luid);
|
||||
int destroy_cluster_cpg(char *uuid);
|
||||
|
||||
int cluster_send(struct clog_request *rq);
|
||||
|
||||
#endif /* _LVM_CLOG_CLUSTER_H */
|
33
daemons/cmirrord/common.h
Normal file
33
daemons/cmirrord/common.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef _LVM_CLOG_COMMON_H
|
||||
#define _LVM_CLOG_COMMON_H
|
||||
|
||||
/*
|
||||
* If there are problems when forking off to become a daemon,
|
||||
* the child will exist with one of these codes. This allows
|
||||
* the parent to know the reason for the failure and print it
|
||||
* to the launching terminal.
|
||||
*
|
||||
* #define EXIT_SUCCESS 0 (from stdlib.h)
|
||||
* #define EXIT_FAILURE 1 (from stdlib.h)
|
||||
*/
|
||||
#define EXIT_LOCKFILE 2
|
||||
#define EXIT_KERNEL_SOCKET 3 /* Failed netlink socket create */
|
||||
#define EXIT_KERNEL_BIND 4
|
||||
#define EXIT_KERNEL_SETSOCKOPT 5
|
||||
#define EXIT_CLUSTER_CKPT_INIT 6 /* Failed to init checkpoint */
|
||||
#define EXIT_QUEUE_NOMEM 7
|
||||
|
||||
#define DM_ULOG_REQUEST_SIZE 1024
|
||||
|
||||
#endif /* _LVM_CLOG_COMMON_H */
|
210
daemons/cmirrord/compat.c
Normal file
210
daemons/cmirrord/compat.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*/
|
||||
#include "logging.h"
|
||||
#include "cluster.h"
|
||||
#include "compat.h"
|
||||
#include "lib/mm/xlate.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* Older versions of the log daemon communicate with different
|
||||
* versions of the inter-machine communication structure, which
|
||||
* varies in size and fields. The older versions append the
|
||||
* standard upstream version of the structure to every request.
|
||||
* COMPAT_OFFSET is where the upstream structure starts.
|
||||
*/
|
||||
#define COMPAT_OFFSET 256
|
||||
|
||||
static void v5_data_endian_switch(struct clog_request *rq, int to_network __attribute__((unused)))
|
||||
{
|
||||
int i, end;
|
||||
int64_t *pi64;
|
||||
uint64_t *pu64;
|
||||
uint32_t rq_type = rq->u_rq.request_type & ~DM_ULOG_RESPONSE;
|
||||
|
||||
if (rq->u_rq.request_type & DM_ULOG_RESPONSE) {
|
||||
switch (rq_type) {
|
||||
case DM_ULOG_CTR:
|
||||
case DM_ULOG_DTR:
|
||||
LOG_ERROR("Invalid response type in endian switch");
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
case DM_ULOG_PRESUSPEND:
|
||||
case DM_ULOG_POSTSUSPEND:
|
||||
case DM_ULOG_RESUME:
|
||||
case DM_ULOG_FLUSH:
|
||||
case DM_ULOG_MARK_REGION:
|
||||
case DM_ULOG_CLEAR_REGION:
|
||||
case DM_ULOG_SET_REGION_SYNC:
|
||||
case DM_ULOG_CHECKPOINT_READY:
|
||||
case DM_ULOG_MEMBER_JOIN:
|
||||
case DM_ULOG_STATUS_INFO:
|
||||
case DM_ULOG_STATUS_TABLE:
|
||||
/* No outbound data */
|
||||
break;
|
||||
|
||||
case DM_ULOG_GET_REGION_SIZE:
|
||||
case DM_ULOG_GET_SYNC_COUNT:
|
||||
pu64 = (uint64_t *)rq->u_rq.data;
|
||||
*pu64 = xlate64(*pu64);
|
||||
break;
|
||||
case DM_ULOG_IS_CLEAN:
|
||||
case DM_ULOG_IN_SYNC:
|
||||
pi64 = (int64_t *)rq->u_rq.data;
|
||||
*pi64 = xlate64(*pi64);
|
||||
break;
|
||||
case DM_ULOG_GET_RESYNC_WORK:
|
||||
case DM_ULOG_IS_REMOTE_RECOVERING:
|
||||
pi64 = (int64_t *)rq->u_rq.data;
|
||||
pu64 = ((uint64_t *)rq->u_rq.data) + 1;
|
||||
*pi64 = xlate64(*pi64);
|
||||
*pu64 = xlate64(*pu64);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown request type, %u", rq_type);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
switch (rq_type) {
|
||||
case DM_ULOG_CTR:
|
||||
case DM_ULOG_DTR:
|
||||
LOG_ERROR("Invalid request type in endian switch");
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
case DM_ULOG_PRESUSPEND:
|
||||
case DM_ULOG_POSTSUSPEND:
|
||||
case DM_ULOG_RESUME:
|
||||
case DM_ULOG_GET_REGION_SIZE:
|
||||
case DM_ULOG_FLUSH:
|
||||
case DM_ULOG_GET_RESYNC_WORK:
|
||||
case DM_ULOG_GET_SYNC_COUNT:
|
||||
case DM_ULOG_STATUS_INFO:
|
||||
case DM_ULOG_STATUS_TABLE:
|
||||
case DM_ULOG_CHECKPOINT_READY:
|
||||
case DM_ULOG_MEMBER_JOIN:
|
||||
/* No incoming data */
|
||||
break;
|
||||
case DM_ULOG_IS_CLEAN:
|
||||
case DM_ULOG_IN_SYNC:
|
||||
case DM_ULOG_IS_REMOTE_RECOVERING:
|
||||
pu64 = (uint64_t *)rq->u_rq.data;
|
||||
*pu64 = xlate64(*pu64);
|
||||
break;
|
||||
case DM_ULOG_MARK_REGION:
|
||||
case DM_ULOG_CLEAR_REGION:
|
||||
end = rq->u_rq.data_size/sizeof(uint64_t);
|
||||
|
||||
pu64 = (uint64_t *)rq->u_rq.data;
|
||||
for (i = 0; i < end; i++)
|
||||
pu64[i] = xlate64(pu64[i]);
|
||||
break;
|
||||
case DM_ULOG_SET_REGION_SYNC:
|
||||
pu64 = (uint64_t *)rq->u_rq.data;
|
||||
pi64 = ((int64_t *)rq->u_rq.data) + 1;
|
||||
*pu64 = xlate64(*pu64);
|
||||
*pi64 = xlate64(*pi64);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown request type, %u", rq_type);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int v5_endian_to_network(struct clog_request *rq)
|
||||
{
|
||||
int size;
|
||||
struct dm_ulog_request *u_rq = &rq->u_rq;
|
||||
|
||||
size = sizeof(*rq) + u_rq->data_size;
|
||||
|
||||
u_rq->error = xlate32(u_rq->error);
|
||||
u_rq->seq = xlate32(u_rq->seq);
|
||||
|
||||
rq->originator = xlate32(rq->originator);
|
||||
|
||||
v5_data_endian_switch(rq, 1);
|
||||
|
||||
u_rq->request_type = xlate32(u_rq->request_type);
|
||||
u_rq->data_size = xlate32(u_rq->data_size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int clog_request_to_network(struct clog_request *rq)
|
||||
{
|
||||
int r;
|
||||
|
||||
/* FIXME: Remove this safety check */
|
||||
if (rq->u.version[0] != xlate64(rq->u.version[1])) {
|
||||
LOG_ERROR("Programmer error: version[0] must be LE");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Are we already running in the endian mode we send
|
||||
* over the wire?
|
||||
*/
|
||||
if (rq->u.version[0] == rq->u.version[1])
|
||||
return 0;
|
||||
|
||||
r = v5_endian_to_network(rq);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v5_endian_from_network(struct clog_request *rq)
|
||||
{
|
||||
int size;
|
||||
struct dm_ulog_request *u_rq = &rq->u_rq;
|
||||
|
||||
u_rq->error = xlate32(u_rq->error);
|
||||
u_rq->seq = xlate32(u_rq->seq);
|
||||
u_rq->request_type = xlate32(u_rq->request_type);
|
||||
u_rq->data_size = xlate32(u_rq->data_size);
|
||||
|
||||
rq->originator = xlate32(rq->originator);
|
||||
|
||||
size = sizeof(*rq) + u_rq->data_size;
|
||||
|
||||
v5_data_endian_switch(rq, 0);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int clog_request_from_network(void *data, size_t data_len)
|
||||
{
|
||||
uint64_t *vp = data;
|
||||
uint64_t version = xlate64(vp[0]);
|
||||
struct clog_request *rq = data;
|
||||
|
||||
switch (version) {
|
||||
case 5: /* Upstream */
|
||||
if (version == vp[0])
|
||||
return 0;
|
||||
break;
|
||||
case 4: /* RHEL 5.[45] */
|
||||
case 3: /* RHEL 5.3 */
|
||||
case 2: /* RHEL 5.2 */
|
||||
/* FIXME: still need to account for payload */
|
||||
if (data_len < (COMPAT_OFFSET + sizeof(*rq)))
|
||||
return -ENOSPC;
|
||||
|
||||
rq = (struct clog_request *)((char *)data + COMPAT_OFFSET);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unable to process cluster message: "
|
||||
"Incompatible version");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
v5_endian_from_network(rq);
|
||||
return 0;
|
||||
}
|
29
daemons/cmirrord/compat.h
Normal file
29
daemons/cmirrord/compat.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*/
|
||||
#ifndef _LVM_CLOG_COMPAT_H
|
||||
#define _LVM_CLOG_COMPAT_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* The intermachine communication structure version are:
|
||||
* 0: Unused
|
||||
* 1: Never in the wild
|
||||
* 2: RHEL 5.2
|
||||
* 3: RHEL 5.3
|
||||
* 4: RHEL 5.4, RHEL 5.5
|
||||
* 5: RHEL 6, Current Upstream Format
|
||||
*/
|
||||
#define CLOG_TFR_VERSION 5
|
||||
|
||||
struct clog_request;
|
||||
|
||||
int clog_request_to_network(struct clog_request *rq);
|
||||
int clog_request_from_network(void *data, size_t data_len);
|
||||
|
||||
#endif /* _LVM_CLOG_COMPAT_H */
|
1967
daemons/cmirrord/functions.c
Normal file
1967
daemons/cmirrord/functions.c
Normal file
File diff suppressed because it is too large
Load Diff
36
daemons/cmirrord/functions.h
Normal file
36
daemons/cmirrord/functions.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef _LVM_CLOG_FUNCTIONS_H
|
||||
#define _LVM_CLOG_FUNCTIONS_H
|
||||
|
||||
#include "libdm/libdevmapper.h"
|
||||
#include "libdm/dm-tools/util.h"
|
||||
#include "libdm/misc/dm-log-userspace.h"
|
||||
#include "cluster.h"
|
||||
|
||||
#define LOG_RESUMED 1
|
||||
#define LOG_SUSPENDED 2
|
||||
|
||||
int local_resume(struct dm_ulog_request *rq);
|
||||
int cluster_postsuspend(char *, uint64_t);
|
||||
|
||||
int do_request(struct clog_request *rq, int server);
|
||||
int push_state(const char *uuid, uint64_t luid,
|
||||
const char *which, char **buf, uint32_t debug_who);
|
||||
int pull_state(const char *uuid, uint64_t luid,
|
||||
const char *which, char *buf, int size);
|
||||
|
||||
int log_get_state(struct dm_ulog_request *rq);
|
||||
int log_status(void);
|
||||
void log_debug(void);
|
||||
|
||||
#endif /* _LVM_CLOG_FUNCTIONS_H */
|
151
daemons/cmirrord/link_mon.c
Normal file
151
daemons/cmirrord/link_mon.c
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "logging.h"
|
||||
#include "link_mon.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct link_callback {
|
||||
int fd;
|
||||
const char *name;
|
||||
void *data;
|
||||
int (*callback)(void *data);
|
||||
|
||||
struct link_callback *next;
|
||||
};
|
||||
|
||||
static unsigned used_pfds = 0;
|
||||
static unsigned free_pfds = 0;
|
||||
static struct pollfd *pfds = NULL;
|
||||
static struct link_callback *callbacks = NULL;
|
||||
|
||||
int links_register(int fd, const char *name, int (*callback)(void *data), void *data)
|
||||
{
|
||||
unsigned i;
|
||||
struct link_callback *lc;
|
||||
|
||||
for (i = 0; i < used_pfds; i++) {
|
||||
if (fd == pfds[i].fd) {
|
||||
LOG_ERROR("links_register: Duplicate file descriptor");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
lc = malloc(sizeof(*lc));
|
||||
if (!lc)
|
||||
return -ENOMEM;
|
||||
|
||||
lc->fd = fd;
|
||||
lc->name = name;
|
||||
lc->data = data;
|
||||
lc->callback = callback;
|
||||
|
||||
if (!free_pfds) {
|
||||
struct pollfd *tmp;
|
||||
tmp = realloc(pfds, sizeof(struct pollfd) * ((used_pfds*2) + 1));
|
||||
if (!tmp) {
|
||||
free(lc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pfds = tmp;
|
||||
free_pfds = used_pfds + 1;
|
||||
}
|
||||
|
||||
free_pfds--;
|
||||
pfds[used_pfds].fd = fd;
|
||||
pfds[used_pfds].events = POLLIN;
|
||||
pfds[used_pfds].revents = 0;
|
||||
used_pfds++;
|
||||
|
||||
lc->next = callbacks;
|
||||
callbacks = lc;
|
||||
LOG_DBG("Adding %s/%d", lc->name, lc->fd);
|
||||
LOG_DBG(" used_pfds = %u, free_pfds = %u",
|
||||
used_pfds, free_pfds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int links_unregister(int fd)
|
||||
{
|
||||
unsigned i;
|
||||
struct link_callback *p, *c;
|
||||
|
||||
for (i = 0; i < used_pfds; i++)
|
||||
if (fd == pfds[i].fd) {
|
||||
/* entire struct is copied (overwritten) */
|
||||
pfds[i] = pfds[used_pfds - 1];
|
||||
used_pfds--;
|
||||
free_pfds++;
|
||||
}
|
||||
|
||||
for (p = NULL, c = callbacks; c; p = c, c = c->next)
|
||||
if (fd == c->fd) {
|
||||
LOG_DBG("Freeing up %s/%d", c->name, c->fd);
|
||||
LOG_DBG(" used_pfds = %u, free_pfds = %u",
|
||||
used_pfds, free_pfds);
|
||||
if (p)
|
||||
p->next = c->next;
|
||||
else
|
||||
callbacks = c->next;
|
||||
free(c);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int links_monitor(void)
|
||||
{
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
for (i = 0; i < used_pfds; i++) {
|
||||
pfds[i].revents = 0;
|
||||
}
|
||||
|
||||
r = poll(pfds, used_pfds, -1);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = 0;
|
||||
/* FIXME: handle POLLHUP */
|
||||
for (i = 0; i < used_pfds; i++)
|
||||
if (pfds[i].revents & POLLIN) {
|
||||
LOG_DBG("Data ready on %d", pfds[i].fd);
|
||||
|
||||
/* FIXME: Add this back return 1;*/
|
||||
r++;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int links_issue_callbacks(void)
|
||||
{
|
||||
unsigned i;
|
||||
struct link_callback *lc;
|
||||
|
||||
for (i = 0; i < used_pfds; i++)
|
||||
if (pfds[i].revents & POLLIN)
|
||||
for (lc = callbacks; lc; lc = lc->next)
|
||||
if (pfds[i].fd == lc->fd) {
|
||||
LOG_DBG("Issuing callback on %s/%d",
|
||||
lc->name, lc->fd);
|
||||
lc->callback(lc->data);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
20
daemons/cmirrord/link_mon.h
Normal file
20
daemons/cmirrord/link_mon.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef _LVM_CLOG_LINK_MON_H
|
||||
#define _LVM_CLOG_LINK_MON_H
|
||||
|
||||
int links_register(int fd, const char *name, int (*callback)(void *data), void *data);
|
||||
int links_unregister(int fd);
|
||||
int links_monitor(void);
|
||||
int links_issue_callbacks(void);
|
||||
|
||||
#endif /* _LVM_CLOG_LINK_MON_H */
|
424
daemons/cmirrord/local.c
Normal file
424
daemons/cmirrord/local.c
Normal file
@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "logging.h"
|
||||
#include "common.h"
|
||||
#include "functions.h"
|
||||
#include "link_mon.h"
|
||||
#include "local.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/connector.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef CN_IDX_DM
|
||||
/* Kernel 2.6.31 is required to run this code */
|
||||
#define CN_IDX_DM 0x7 /* Device Mapper */
|
||||
#define CN_VAL_DM_USERSPACE_LOG 0x1
|
||||
#endif
|
||||
|
||||
static int cn_fd = -1; /* Connector (netlink) socket fd */
|
||||
static char recv_buf[2048];
|
||||
static char send_buf[2048];
|
||||
|
||||
|
||||
/* FIXME: merge this function with kernel_send_helper */
|
||||
static int kernel_ack(uint32_t seq, int error)
|
||||
{
|
||||
int r;
|
||||
struct nlmsghdr *nlh = (struct nlmsghdr *)send_buf;
|
||||
struct cn_msg *msg = NLMSG_DATA(nlh);
|
||||
|
||||
if (error < 0) {
|
||||
LOG_ERROR("Programmer error: error codes must be positive");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(send_buf, 0, sizeof(send_buf));
|
||||
|
||||
nlh->nlmsg_seq = 0;
|
||||
nlh->nlmsg_pid = getpid();
|
||||
nlh->nlmsg_type = NLMSG_DONE;
|
||||
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct cn_msg));
|
||||
nlh->nlmsg_flags = 0;
|
||||
|
||||
msg->len = 0;
|
||||
msg->id.idx = CN_IDX_DM;
|
||||
msg->id.val = CN_VAL_DM_USERSPACE_LOG;
|
||||
msg->seq = seq;
|
||||
msg->ack = error;
|
||||
|
||||
r = send(cn_fd, nlh, NLMSG_LENGTH(sizeof(struct cn_msg)), 0);
|
||||
/* FIXME: do better error processing */
|
||||
if (r <= 0)
|
||||
return -EBADE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* kernel_recv
|
||||
* @rq: the newly allocated request from kernel
|
||||
*
|
||||
* Read requests from the kernel and allocate space for the new request.
|
||||
* If there is no request from the kernel, *rq is NULL.
|
||||
*
|
||||
* This function is not thread safe due to returned stack pointer. In fact,
|
||||
* the returned pointer must not be in-use when this function is called again.
|
||||
*
|
||||
* Returns: 0 on success, -EXXX on error
|
||||
*/
|
||||
static int kernel_recv(struct clog_request **rq)
|
||||
{
|
||||
int r = 0;
|
||||
ssize_t len;
|
||||
char *foo;
|
||||
struct cn_msg *msg;
|
||||
struct dm_ulog_request *u_rq;
|
||||
struct nlmsghdr *nlmsg_h;
|
||||
|
||||
*rq = NULL;
|
||||
memset(recv_buf, 0, sizeof(recv_buf));
|
||||
|
||||
len = recv(cn_fd, recv_buf, sizeof(recv_buf), 0);
|
||||
if (len < 0) {
|
||||
LOG_ERROR("Failed to recv message from kernel");
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nlmsg_h = (struct nlmsghdr *)recv_buf;
|
||||
switch (nlmsg_h->nlmsg_type) {
|
||||
case NLMSG_ERROR:
|
||||
LOG_ERROR("Unable to recv message from kernel: NLMSG_ERROR");
|
||||
r = -EBADE;
|
||||
goto fail;
|
||||
case NLMSG_DONE:
|
||||
msg = (struct cn_msg *)NLMSG_DATA((struct nlmsghdr *)recv_buf);
|
||||
len -= (ssize_t)sizeof(struct nlmsghdr);
|
||||
|
||||
if (len < (ssize_t)sizeof(struct cn_msg)) {
|
||||
LOG_ERROR("Incomplete request from kernel received");
|
||||
r = -EBADE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (msg->len > DM_ULOG_REQUEST_SIZE) {
|
||||
LOG_ERROR("Not enough space to receive kernel request (%d/%d)",
|
||||
msg->len, DM_ULOG_REQUEST_SIZE);
|
||||
r = -EBADE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!msg->len)
|
||||
LOG_ERROR("Zero length message received");
|
||||
|
||||
len -= (ssize_t)sizeof(struct cn_msg);
|
||||
|
||||
if (len < msg->len)
|
||||
LOG_ERROR("len = %zd, msg->len = %" PRIu16, len, msg->len);
|
||||
|
||||
msg->data[msg->len] = '\0'; /* Cleaner way to ensure this? */
|
||||
u_rq = (struct dm_ulog_request *)msg->data;
|
||||
|
||||
if (!u_rq->request_type) {
|
||||
LOG_DBG("Bad transmission, requesting resend [%u]",
|
||||
msg->seq);
|
||||
r = -EAGAIN;
|
||||
|
||||
if (kernel_ack(msg->seq, EAGAIN)) {
|
||||
LOG_ERROR("Failed to NACK kernel transmission [%u]",
|
||||
msg->seq);
|
||||
r = -EBADE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we've got sizeof(struct cn_msg) + sizeof(struct nlmsghdr)
|
||||
* worth of space that precede the request structure from the
|
||||
* kernel. Since that space isn't going to be used again, we
|
||||
* can take it for our purposes; rather than allocating a whole
|
||||
* new structure and doing a memcpy.
|
||||
*
|
||||
* We should really make sure 'clog_request' doesn't grow
|
||||
* beyond what is available to us, but we need only check it
|
||||
* once... perhaps at compile time?
|
||||
*/
|
||||
foo = (char *)u_rq;
|
||||
foo -= (sizeof(struct clog_request) - sizeof(struct dm_ulog_request));
|
||||
*rq = (struct clog_request *) foo;
|
||||
|
||||
/* Clear the wrapper container fields */
|
||||
memset(*rq, 0, (size_t)((char *)u_rq - (char *)(*rq)));
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown nlmsg_type");
|
||||
r = -EBADE;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (r)
|
||||
*rq = NULL;
|
||||
|
||||
return (r == -EAGAIN) ? 0 : r;
|
||||
}
|
||||
|
||||
static int kernel_send_helper(void *data, uint16_t out_size)
|
||||
{
|
||||
int r;
|
||||
struct nlmsghdr *nlh;
|
||||
struct cn_msg *msg;
|
||||
|
||||
memset(send_buf, 0, sizeof(send_buf));
|
||||
|
||||
nlh = (struct nlmsghdr *)send_buf;
|
||||
nlh->nlmsg_seq = 0; /* FIXME: Is this used? */
|
||||
nlh->nlmsg_pid = getpid();
|
||||
nlh->nlmsg_type = NLMSG_DONE;
|
||||
nlh->nlmsg_len = NLMSG_LENGTH(out_size + sizeof(struct cn_msg));
|
||||
nlh->nlmsg_flags = 0;
|
||||
|
||||
msg = NLMSG_DATA(nlh);
|
||||
memcpy(msg->data, data, out_size);
|
||||
msg->len = out_size;
|
||||
msg->id.idx = CN_IDX_DM;
|
||||
msg->id.val = CN_VAL_DM_USERSPACE_LOG;
|
||||
msg->seq = 0;
|
||||
|
||||
r = send(cn_fd, nlh, NLMSG_LENGTH(out_size + sizeof(struct cn_msg)), 0);
|
||||
/* FIXME: do better error processing */
|
||||
if (r <= 0)
|
||||
return -EBADE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* do_local_work
|
||||
*
|
||||
* Any processing errors are placed in the 'rq'
|
||||
* structure to be reported back to the kernel.
|
||||
* It may be pointless for this function to
|
||||
* return an int.
|
||||
*
|
||||
* Returns: 0 on success, -EXXX on failure
|
||||
*/
|
||||
static int do_local_work(void *data __attribute__((unused)))
|
||||
{
|
||||
int r;
|
||||
struct clog_request *rq;
|
||||
struct dm_ulog_request *u_rq = NULL;
|
||||
|
||||
r = kernel_recv(&rq);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (!rq)
|
||||
return 0;
|
||||
|
||||
u_rq = &rq->u_rq;
|
||||
LOG_DBG("[%s] Request from kernel received: [%s/%u]",
|
||||
SHORT_UUID(u_rq->uuid), RQ_TYPE(u_rq->request_type),
|
||||
u_rq->seq);
|
||||
switch (u_rq->request_type) {
|
||||
case DM_ULOG_CTR:
|
||||
case DM_ULOG_DTR:
|
||||
case DM_ULOG_GET_REGION_SIZE:
|
||||
case DM_ULOG_IN_SYNC:
|
||||
case DM_ULOG_GET_SYNC_COUNT:
|
||||
case DM_ULOG_STATUS_TABLE:
|
||||
case DM_ULOG_PRESUSPEND:
|
||||
/* We do not specify ourselves as server here */
|
||||
r = do_request(rq, 0);
|
||||
if (r)
|
||||
LOG_DBG("Returning failed request to kernel [%s]",
|
||||
RQ_TYPE(u_rq->request_type));
|
||||
r = kernel_send(u_rq);
|
||||
if (r)
|
||||
LOG_ERROR("Failed to respond to kernel [%s]",
|
||||
RQ_TYPE(u_rq->request_type));
|
||||
|
||||
break;
|
||||
case DM_ULOG_RESUME:
|
||||
/*
|
||||
* Resume is a special case that requires a local
|
||||
* component to join the CPG, and a cluster component
|
||||
* to handle the request.
|
||||
*/
|
||||
r = local_resume(u_rq);
|
||||
if (r) {
|
||||
LOG_DBG("Returning failed request to kernel [%s]",
|
||||
RQ_TYPE(u_rq->request_type));
|
||||
r = kernel_send(u_rq);
|
||||
if (r)
|
||||
LOG_ERROR("Failed to respond to kernel [%s]",
|
||||
RQ_TYPE(u_rq->request_type));
|
||||
break;
|
||||
}
|
||||
/* ELSE */ /* fall through */
|
||||
case DM_ULOG_IS_CLEAN:
|
||||
case DM_ULOG_FLUSH:
|
||||
case DM_ULOG_MARK_REGION:
|
||||
case DM_ULOG_GET_RESYNC_WORK:
|
||||
case DM_ULOG_SET_REGION_SYNC:
|
||||
case DM_ULOG_STATUS_INFO:
|
||||
case DM_ULOG_IS_REMOTE_RECOVERING:
|
||||
case DM_ULOG_POSTSUSPEND:
|
||||
r = cluster_send(rq);
|
||||
if (r) {
|
||||
u_rq->data_size = 0;
|
||||
u_rq->error = r;
|
||||
if (kernel_send(u_rq))
|
||||
LOG_ERROR("Failed to respond to kernel [%s]",
|
||||
RQ_TYPE(u_rq->request_type));
|
||||
}
|
||||
|
||||
break;
|
||||
case DM_ULOG_CLEAR_REGION:
|
||||
r = kernel_ack(u_rq->seq, 0);
|
||||
|
||||
r = cluster_send(rq);
|
||||
if (r) {
|
||||
/*
|
||||
* FIXME: store error for delivery on flush
|
||||
* This would allow us to optimize MARK_REGION
|
||||
* too.
|
||||
*/
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Invalid log request received (%u), ignoring.",
|
||||
u_rq->request_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (r && !u_rq->error)
|
||||
u_rq->error = r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* kernel_send
|
||||
* @u_rq: result to pass back to kernel
|
||||
*
|
||||
* This function returns the u_rq structure
|
||||
* (containing the results) to the kernel.
|
||||
* It then frees the structure.
|
||||
*
|
||||
* WARNING: should the structure be freed if
|
||||
* there is an error? I vote 'yes'. If the
|
||||
* kernel doesn't get the response, it should
|
||||
* resend the request.
|
||||
*
|
||||
* Returns: 0 on success, -EXXX on failure
|
||||
*/
|
||||
int kernel_send(struct dm_ulog_request *u_rq)
|
||||
{
|
||||
int r;
|
||||
uint16_t size;
|
||||
|
||||
if (!u_rq)
|
||||
return -EINVAL;
|
||||
|
||||
size = (uint16_t)(sizeof(struct dm_ulog_request) + u_rq->data_size);
|
||||
|
||||
if (!u_rq->data_size && !u_rq->error) {
|
||||
/* An ACK is all that is needed */
|
||||
|
||||
/* FIXME: add ACK code */
|
||||
} else if (size > DM_ULOG_REQUEST_SIZE) {
|
||||
/*
|
||||
* If we gotten here, we've already overrun
|
||||
* our allotted space somewhere.
|
||||
*
|
||||
* We must do something, because the kernel
|
||||
* is waiting for a response.
|
||||
*/
|
||||
LOG_ERROR("Not enough space to respond to server");
|
||||
u_rq->error = -ENOSPC;
|
||||
size = sizeof(struct dm_ulog_request);
|
||||
}
|
||||
|
||||
r = kernel_send_helper(u_rq, size);
|
||||
if (r)
|
||||
LOG_ERROR("Failed to send msg to kernel.");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* init_local
|
||||
*
|
||||
* Initialize kernel communication socket (netlink)
|
||||
*
|
||||
* Returns: 0 on success, values from common.h on failure
|
||||
*/
|
||||
int init_local(void)
|
||||
{
|
||||
int r = 0;
|
||||
unsigned opt;
|
||||
struct sockaddr_nl addr;
|
||||
|
||||
cn_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
|
||||
if (cn_fd < 0)
|
||||
return EXIT_KERNEL_SOCKET;
|
||||
|
||||
/* memset to fix valgrind complaint */
|
||||
memset(&addr, 0, sizeof(struct sockaddr_nl));
|
||||
|
||||
addr.nl_family = AF_NETLINK;
|
||||
addr.nl_groups = CN_IDX_DM;
|
||||
addr.nl_pid = 0;
|
||||
|
||||
r = bind(cn_fd, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (r < 0) {
|
||||
if (close(cn_fd))
|
||||
LOG_ERROR("Failed to close socket: %s",
|
||||
strerror(errno));
|
||||
return EXIT_KERNEL_BIND;
|
||||
}
|
||||
|
||||
opt = addr.nl_groups;
|
||||
r = setsockopt(cn_fd, 270, NETLINK_ADD_MEMBERSHIP, &opt, sizeof(opt));
|
||||
if (r) {
|
||||
if (close(cn_fd))
|
||||
LOG_ERROR("Failed to close socket: %s",
|
||||
strerror(errno));
|
||||
return EXIT_KERNEL_SETSOCKOPT;
|
||||
}
|
||||
|
||||
/*
|
||||
r = fcntl(cn_fd, F_SETFL, FNDELAY);
|
||||
*/
|
||||
|
||||
links_register(cn_fd, "local", do_local_work, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cleanup_local
|
||||
*
|
||||
* Clean up before exiting
|
||||
*/
|
||||
void cleanup_local(void)
|
||||
{
|
||||
links_unregister(cn_fd);
|
||||
if (cn_fd >= 0 && close(cn_fd))
|
||||
LOG_ERROR("Failed to close socket: %s",
|
||||
strerror(errno));
|
||||
}
|
22
daemons/cmirrord/local.h
Normal file
22
daemons/cmirrord/local.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef _LVM_CLOG_LOCAL_H
|
||||
#define _LVM_CLOG_LOCAL_H
|
||||
|
||||
struct dm_ulog_request;
|
||||
|
||||
int init_local(void);
|
||||
void cleanup_local(void);
|
||||
|
||||
int kernel_send(struct dm_ulog_request *rq);
|
||||
|
||||
#endif /* _LVM_CLOG_LOCAL_H */
|
57
daemons/cmirrord/logging.c
Normal file
57
daemons/cmirrord/logging.c
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "logging.h"
|
||||
|
||||
const char * const __rq_types_off_by_one[] = {
|
||||
"DM_ULOG_CTR",
|
||||
"DM_ULOG_DTR",
|
||||
"DM_ULOG_PRESUSPEND",
|
||||
"DM_ULOG_POSTSUSPEND",
|
||||
"DM_ULOG_RESUME",
|
||||
"DM_ULOG_GET_REGION_SIZE",
|
||||
"DM_ULOG_IS_CLEAN",
|
||||
"DM_ULOG_IN_SYNC",
|
||||
"DM_ULOG_FLUSH",
|
||||
"DM_ULOG_MARK_REGION",
|
||||
"DM_ULOG_CLEAR_REGION",
|
||||
"DM_ULOG_GET_RESYNC_WORK",
|
||||
"DM_ULOG_SET_REGION_SYNC",
|
||||
"DM_ULOG_GET_SYNC_COUNT",
|
||||
"DM_ULOG_STATUS_INFO",
|
||||
"DM_ULOG_STATUS_TABLE",
|
||||
"DM_ULOG_IS_REMOTE_RECOVERING",
|
||||
NULL
|
||||
};
|
||||
|
||||
int log_tabbing = 0;
|
||||
int log_is_open = 0;
|
||||
|
||||
/*
|
||||
* Variables for various conditional logging
|
||||
*/
|
||||
#ifdef MEMB
|
||||
int log_membership_change = 1;
|
||||
#else
|
||||
int log_membership_change = 0;
|
||||
#endif
|
||||
|
||||
#ifdef CKPT
|
||||
int log_checkpoint = 1;
|
||||
#else
|
||||
int log_checkpoint = 0;
|
||||
#endif
|
||||
|
||||
#ifdef RESEND
|
||||
int log_resend_requests = 1;
|
||||
#else
|
||||
int log_resend_requests = 0;
|
||||
#endif
|
73
daemons/cmirrord/logging.h
Normal file
73
daemons/cmirrord/logging.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_CLOG_LOGGING_H
|
||||
#define _LVM_CLOG_LOGGING_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <syslog.h>
|
||||
|
||||
/* SHORT_UUID - print last 8 chars of a string */
|
||||
#define SHORT_UUID(x) (strlen(x) > 8) ? ((x) + (strlen(x) - 8)) : (x)
|
||||
|
||||
extern const char * const __rq_types_off_by_one[];
|
||||
#define RQ_TYPE(x) __rq_types_off_by_one[(x) - 1]
|
||||
|
||||
extern int log_tabbing;
|
||||
extern int log_is_open;
|
||||
extern int log_membership_change;
|
||||
extern int log_checkpoint;
|
||||
extern int log_resend_requests;
|
||||
|
||||
#define LOG_OPEN(ident, option, facility) do { \
|
||||
openlog(ident, option, facility); \
|
||||
log_is_open = 1; \
|
||||
} while (0)
|
||||
|
||||
#define LOG_CLOSE(void) do { \
|
||||
log_is_open = 0; \
|
||||
closelog(); \
|
||||
} while (0)
|
||||
|
||||
#define LOG_OUTPUT(level, f, arg...) do { \
|
||||
int __i; \
|
||||
char __buffer[16]; \
|
||||
FILE *fp = (level > LOG_NOTICE) ? stderr : stdout; \
|
||||
if (log_is_open) { \
|
||||
for (__i = 0; (__i < log_tabbing) && (__i < 15); __i++) \
|
||||
__buffer[__i] = '\t'; \
|
||||
__buffer[__i] = '\0'; \
|
||||
syslog(level, "%s" f "\n", __buffer, ## arg); \
|
||||
} else { \
|
||||
for (__i = 0; __i < log_tabbing; __i++) \
|
||||
fprintf(fp, "\t"); \
|
||||
fprintf(fp, f "\n", ## arg); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
#define LOG_DBG(f, arg...) LOG_OUTPUT(LOG_DEBUG, f, ## arg)
|
||||
#else /* DEBUG */
|
||||
#define LOG_DBG(f, arg...) do {} while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#define LOG_COND(__X, f, arg...) do {\
|
||||
if (__X) { \
|
||||
LOG_OUTPUT(LOG_NOTICE, f, ## arg); \
|
||||
} \
|
||||
} while (0)
|
||||
#define LOG_PRINT(f, arg...) LOG_OUTPUT(LOG_NOTICE, f, ## arg)
|
||||
#define LOG_ERROR(f, arg...) LOG_OUTPUT(LOG_ERR, f, ## arg)
|
||||
|
||||
#endif /* _LVM_CLOG_LOGGING_H */
|
4
daemons/dmeventd/.exported_symbols
Normal file
4
daemons/dmeventd/.exported_symbols
Normal file
@ -0,0 +1,4 @@
|
||||
init_fifos
|
||||
fini_fifos
|
||||
daemon_talk
|
||||
dm_event_get_version
|
1
daemons/dmeventd/.gitignore
vendored
Normal file
1
daemons/dmeventd/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
dmeventd
|
116
daemons/dmeventd/Makefile.in
Normal file
116
daemons/dmeventd/Makefile.in
Normal file
@ -0,0 +1,116 @@
|
||||
#
|
||||
# Copyright (C) 2005-2011 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of the device-mapper userspace tools.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU Lesser General Public License v.2.1.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
|
||||
SOURCES = libdevmapper-event.c
|
||||
SOURCES2 = dmeventd.c
|
||||
|
||||
TARGETS = dmeventd
|
||||
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES) $(SOURCES2) \
|
||||
plugins/lvm2/dmeventd_lvm.c \
|
||||
plugins/mirror/dmeventd_mirror.c \
|
||||
plugins/raid/dmeventd_raid.c \
|
||||
plugins/snapshot/dmeventd_snapshot.c \
|
||||
plugins/thin/dmeventd_thin.c \
|
||||
plugins/vdo/dmeventd_vdo.c \
|
||||
)
|
||||
CFLOW_TARGET := $(TARGETS)
|
||||
|
||||
.PHONY: install_lib_dynamic install_lib_static install_include \
|
||||
install_pkgconfig install_dmeventd_dynamic install_dmeventd_static \
|
||||
install_lib install_dmeventd
|
||||
|
||||
INSTALL_DMEVENTD_TARGETS = install_dmeventd_dynamic
|
||||
INSTALL_LIB_TARGETS = install_lib_dynamic
|
||||
|
||||
LIB_NAME = libdevmapper-event
|
||||
ifeq ("@STATIC_LINK@", "yes")
|
||||
LIB_STATIC = $(LIB_NAME).a
|
||||
TARGETS += $(LIB_STATIC) dmeventd.static
|
||||
INSTALL_DMEVENTD_TARGETS += install_dmeventd_static
|
||||
INSTALL_LIB_TARGETS += install_lib_static
|
||||
endif
|
||||
|
||||
LIB_VERSION = $(LIB_VERSION_DM)
|
||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
||||
LIBS = $(PTHREAD_LIBS) -L$(interfacebuilddir) -ldevmapper
|
||||
|
||||
CLEAN_TARGETS = dmeventd.static $(LIB_NAME).a
|
||||
|
||||
ifneq ($(MAKECMDGOALS),device-mapper)
|
||||
SUBDIRS+=plugins
|
||||
endif
|
||||
|
||||
CFLOW_LIST = $(SOURCES)
|
||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
||||
|
||||
EXPORTED_HEADER = $(srcdir)/libdevmapper-event.h
|
||||
EXPORTED_FN_PREFIX = dm_event
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
all: device-mapper
|
||||
device-mapper: $(TARGETS)
|
||||
plugins.device-mapper: $(LIB_SHARED)
|
||||
|
||||
CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS)
|
||||
|
||||
dmeventd: $(LIB_SHARED) dmeventd.o
|
||||
$(SHOW) " [CC] $@"
|
||||
$(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) dmeventd.o \
|
||||
-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS)
|
||||
|
||||
dmeventd.static: $(LIB_STATIC) dmeventd.o
|
||||
$(SHOW) " [CC] $@"
|
||||
$(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(STATIC_LDFLAGS) -static dmeventd.o \
|
||||
-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS) $(STATIC_LIBS)
|
||||
|
||||
ifeq ("@PKGCONFIG@", "yes")
|
||||
INSTALL_LIB_TARGETS += install_pkgconfig
|
||||
endif
|
||||
|
||||
install_include: $(srcdir)/libdevmapper-event.h
|
||||
$(SHOW) " [INSTALL] $(<F)"
|
||||
$(Q) $(INSTALL_DATA) -D $< $(includedir)/$(<F)
|
||||
|
||||
install_pkgconfig: libdevmapper-event.pc
|
||||
$(SHOW) " [INSTALL] $<"
|
||||
$(Q) $(INSTALL_DATA) -D $< $(pkgconfigdir)/devmapper-event.pc
|
||||
|
||||
install_lib_dynamic: install_lib_shared
|
||||
|
||||
install_lib_static: $(LIB_STATIC)
|
||||
$(SHOW) " [INSTALL] $<"
|
||||
$(Q) $(INSTALL_DATA) -D $< $(usrlibdir)/$(<F)
|
||||
|
||||
install_lib: $(INSTALL_LIB_TARGETS)
|
||||
|
||||
install_dmeventd_dynamic: dmeventd
|
||||
$(SHOW) " [INSTALL] $<"
|
||||
$(Q) $(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
||||
|
||||
install_dmeventd_static: dmeventd.static
|
||||
$(SHOW) " [INSTALL] $<"
|
||||
$(Q) $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
|
||||
|
||||
install_dmeventd: $(INSTALL_DMEVENTD_TARGETS)
|
||||
|
||||
install: install_include install_lib install_dmeventd
|
||||
|
||||
install_device-mapper: install_include install_lib install_dmeventd
|
||||
|
||||
DISTCLEAN_TARGETS += libdevmapper-event.pc
|
2463
daemons/dmeventd/dmeventd.c
Normal file
2463
daemons/dmeventd/dmeventd.c
Normal file
File diff suppressed because it is too large
Load Diff
78
daemons/dmeventd/dmeventd.h
Normal file
78
daemons/dmeventd/dmeventd.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __DMEVENTD_DOT_H__
|
||||
#define __DMEVENTD_DOT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* FIXME This stuff must be configurable. */
|
||||
|
||||
#define DM_EVENT_FIFO_CLIENT DEFAULT_DM_RUN_DIR "/dmeventd-client"
|
||||
#define DM_EVENT_FIFO_SERVER DEFAULT_DM_RUN_DIR "/dmeventd-server"
|
||||
|
||||
#define DM_EVENT_DEFAULT_TIMEOUT 10
|
||||
|
||||
/* Commands for the daemon passed in the message below. */
|
||||
enum dm_event_command {
|
||||
DM_EVENT_CMD_ACTIVE = 1,
|
||||
DM_EVENT_CMD_REGISTER_FOR_EVENT,
|
||||
DM_EVENT_CMD_UNREGISTER_FOR_EVENT,
|
||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE,
|
||||
DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
|
||||
DM_EVENT_CMD_SET_TIMEOUT,
|
||||
DM_EVENT_CMD_GET_TIMEOUT,
|
||||
DM_EVENT_CMD_HELLO,
|
||||
DM_EVENT_CMD_DIE,
|
||||
DM_EVENT_CMD_GET_STATUS,
|
||||
DM_EVENT_CMD_GET_PARAMETERS,
|
||||
};
|
||||
|
||||
/* Message passed between client and daemon. */
|
||||
struct dm_event_daemon_message {
|
||||
uint32_t cmd;
|
||||
uint32_t size;
|
||||
char *data;
|
||||
};
|
||||
|
||||
/* FIXME Is this meant to be exported? I can't see where the
|
||||
interface uses it. */
|
||||
/* Fifos for client/daemon communication. */
|
||||
struct dm_event_fifos {
|
||||
int client;
|
||||
int server;
|
||||
const char *client_path;
|
||||
const char *server_path;
|
||||
};
|
||||
|
||||
/* EXIT_SUCCESS 0 -- stdlib.h */
|
||||
/* EXIT_FAILURE 1 -- stdlib.h */
|
||||
/* EXIT_LOCKFILE_INUSE 2 -- obsoleted */
|
||||
#define EXIT_DESC_CLOSE_FAILURE 3
|
||||
#define EXIT_DESC_OPEN_FAILURE 4
|
||||
/* EXIT_OPEN_PID_FAILURE 5 -- obsoleted */
|
||||
#define EXIT_FIFO_FAILURE 6
|
||||
#define EXIT_CHDIR_FAILURE 7
|
||||
|
||||
/* Implemented in libdevmapper-event.c, but not part of public API. */
|
||||
// FIXME misuse of bitmask as enum
|
||||
int daemon_talk(struct dm_event_fifos *fifos,
|
||||
struct dm_event_daemon_message *msg, int cmd,
|
||||
const char *dso_name, const char *dev_name,
|
||||
unsigned evmask, uint32_t timeout);
|
||||
int init_fifos(struct dm_event_fifos *fifos);
|
||||
void fini_fifos(struct dm_event_fifos *fifos);
|
||||
int dm_event_get_version(struct dm_event_fifos *fifos, int *version);
|
||||
|
||||
#endif /* __DMEVENTD_DOT_H__ */
|
1050
daemons/dmeventd/libdevmapper-event.c
Normal file
1050
daemons/dmeventd/libdevmapper-event.c
Normal file
File diff suppressed because it is too large
Load Diff
139
daemons/dmeventd/libdevmapper-event.h
Normal file
139
daemons/dmeventd/libdevmapper-event.h
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note that this file is released only as part of a technology preview
|
||||
* and its contents may change in future updates in ways that do not
|
||||
* preserve compatibility.
|
||||
*/
|
||||
|
||||
#ifndef LIB_DMEVENT_H
|
||||
#define LIB_DMEVENT_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Event library interface.
|
||||
*/
|
||||
|
||||
enum dm_event_mask {
|
||||
DM_EVENT_SINGLE = 0x000001, /* Report multiple errors just once. */
|
||||
DM_EVENT_MULTI = 0x000002, /* Report all of them. */
|
||||
DM_EVENT_SETTINGS_MASK = 0x0000FF,
|
||||
|
||||
DM_EVENT_SECTOR_ERROR = 0x000100, /* Failure on a particular sector. */
|
||||
DM_EVENT_DEVICE_ERROR = 0x000200, /* Device failure. */
|
||||
DM_EVENT_PATH_ERROR = 0x000400, /* Failure on an io path. */
|
||||
DM_EVENT_ADAPTOR_ERROR = 0x000800, /* Failure of a host adaptor. */
|
||||
DM_EVENT_ERROR_MASK = 0x00FF00,
|
||||
|
||||
DM_EVENT_SYNC_STATUS = 0x010000, /* Mirror synchronization completed/failed. */
|
||||
DM_EVENT_TIMEOUT = 0x020000, /* Timeout has occurred */
|
||||
|
||||
DM_EVENT_ERROR_AND_TIMEOUT_MASK = 0x02FF00,
|
||||
|
||||
DM_EVENT_STATUS_MASK = 0xFF0000,
|
||||
|
||||
DM_EVENT_REGISTRATION_PENDING = 0x1000000, /* Monitor thread is setting-up/shutting-down */
|
||||
};
|
||||
|
||||
#define DM_EVENT_ALL_ERRORS DM_EVENT_ERROR_MASK
|
||||
#define DM_EVENT_PROTOCOL_VERSION 2
|
||||
|
||||
struct dm_task;
|
||||
struct dm_event_handler;
|
||||
|
||||
struct dm_event_handler *dm_event_handler_create(void);
|
||||
void dm_event_handler_destroy(struct dm_event_handler *dmevh);
|
||||
|
||||
/*
|
||||
* Path of shared library to handle events.
|
||||
*
|
||||
* All of dmeventd, dso, device_name and uuid strings are duplicated so
|
||||
* you do not need to keep the pointers valid after the call succeeds.
|
||||
* They may return -ENOMEM though.
|
||||
*/
|
||||
int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path);
|
||||
|
||||
/*
|
||||
* Path of dmeventd binary.
|
||||
*/
|
||||
int dm_event_handler_set_dmeventd_path(struct dm_event_handler *dmevh, const char *dmeventd_path);
|
||||
|
||||
/*
|
||||
* Identify the device to monitor by exactly one of dev_name, uuid or
|
||||
* device number. String arguments are duplicated, see above.
|
||||
*/
|
||||
int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *dev_name);
|
||||
|
||||
int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid);
|
||||
|
||||
void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major);
|
||||
void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor);
|
||||
void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout);
|
||||
|
||||
/*
|
||||
* Specify mask for events to monitor.
|
||||
*/
|
||||
// FIXME misuse of bitmask as enum
|
||||
void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
|
||||
enum dm_event_mask evmask);
|
||||
|
||||
const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh);
|
||||
const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh);
|
||||
const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh);
|
||||
int dm_event_handler_get_major(const struct dm_event_handler *dmevh);
|
||||
int dm_event_handler_get_minor(const struct dm_event_handler *dmevh);
|
||||
int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh);
|
||||
// FIXME misuse of bitmask as enum
|
||||
enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh);
|
||||
|
||||
/* FIXME Review interface (what about this next thing?) */
|
||||
int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next);
|
||||
|
||||
/*
|
||||
* Initiate monitoring using dmeventd.
|
||||
*/
|
||||
int dm_event_register_handler(const struct dm_event_handler *dmevh);
|
||||
int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
|
||||
|
||||
/* Set debug level for logging, and whether to log on stdout/stderr or syslog */
|
||||
void dm_event_log_set(int debug_log_level, int use_syslog);
|
||||
|
||||
/* Log messages according to current debug level */
|
||||
__attribute__((format(printf, 6, 0)))
|
||||
void dm_event_log(const char *subsys, int level, const char *file,
|
||||
int line, int dm_errno_or_class,
|
||||
const char *format, va_list ap);
|
||||
/* Macro to route print_log do dm_event_log() */
|
||||
#define DM_EVENT_LOG_FN(subsys) \
|
||||
void print_log(int level, const char *file, int line, int dm_errno_or_class,\
|
||||
const char *format, ...)\
|
||||
{\
|
||||
va_list ap;\
|
||||
va_start(ap, format);\
|
||||
dm_event_log(subsys, level, file, line, dm_errno_or_class, format, ap);\
|
||||
va_end(ap);\
|
||||
}
|
||||
|
||||
/* Prototypes for DSO interface, see dmeventd.c, struct dso_data for
|
||||
detailed descriptions. */
|
||||
// FIXME misuse of bitmask as enum
|
||||
void process_event(struct dm_task *dmt, enum dm_event_mask evmask, void **user);
|
||||
int register_device(const char *device_name, const char *uuid, int major, int minor, void **user);
|
||||
int unregister_device(const char *device_name, const char *uuid, int major,
|
||||
int minor, void **user);
|
||||
|
||||
#endif
|
10
daemons/dmeventd/libdevmapper-event.pc.in
Normal file
10
daemons/dmeventd/libdevmapper-event.pc.in
Normal file
@ -0,0 +1,10 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: devmapper-event
|
||||
Description: device-mapper event library
|
||||
Version: @DM_LIB_PATCHLEVEL@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -ldevmapper-event
|
27
daemons/dmeventd/plugins/Makefile.in
Normal file
27
daemons/dmeventd/plugins/Makefile.in
Normal file
@ -0,0 +1,27 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SUBDIRS += lvm2 snapshot raid thin mirror vdo
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
snapshot: lvm2
|
||||
mirror: lvm2
|
||||
raid: lvm2
|
||||
thin: lvm2
|
||||
vdo: lvm2
|
7
daemons/dmeventd/plugins/lvm2/.exported_symbols
Normal file
7
daemons/dmeventd/plugins/lvm2/.exported_symbols
Normal file
@ -0,0 +1,7 @@
|
||||
dmeventd_lvm2_init
|
||||
dmeventd_lvm2_exit
|
||||
dmeventd_lvm2_lock
|
||||
dmeventd_lvm2_unlock
|
||||
dmeventd_lvm2_pool
|
||||
dmeventd_lvm2_run
|
||||
dmeventd_lvm2_command
|
30
daemons/dmeventd/plugins/lvm2/Makefile.in
Normal file
30
daemons/dmeventd/plugins/lvm2/Makefile.in
Normal file
@ -0,0 +1,30 @@
|
||||
#
|
||||
# Copyright (C) 2010-2014 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
CLDFLAGS += -L$(top_builddir)/tools
|
||||
LIBS += $(DMEVENT_LIBS) $(PTHREAD_LIBS) @LVM2CMD_LIB@
|
||||
|
||||
SOURCES = dmeventd_lvm.c
|
||||
|
||||
LIB_SHARED = libdevmapper-event-lvm2.$(LIB_SUFFIX)
|
||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
install_lvm2: install_lib_shared
|
||||
|
||||
install: install_lvm2
|
193
daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c
Normal file
193
daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib/misc/lib.h"
|
||||
#include "dmeventd_lvm.h"
|
||||
#include "daemons/dmeventd/libdevmapper-event.h"
|
||||
#include "tools/lvm2cmd.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
* register_device() is called first and performs initialisation.
|
||||
* Only one device may be registered or unregistered at a time.
|
||||
*/
|
||||
static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* Number of active registrations.
|
||||
*/
|
||||
static int _register_count = 0;
|
||||
static struct dm_pool *_mem_pool = NULL;
|
||||
static void *_lvm_handle = NULL;
|
||||
static DM_LIST_INIT(_env_registry);
|
||||
|
||||
struct env_data {
|
||||
struct dm_list list;
|
||||
const char *cmd;
|
||||
const char *data;
|
||||
};
|
||||
|
||||
DM_EVENT_LOG_FN("#lvm")
|
||||
|
||||
static void _lvm2_print_log(int level, const char *file, int line,
|
||||
int dm_errno_or_class, const char *msg)
|
||||
{
|
||||
print_log(level, file, line, dm_errno_or_class, "%s", msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently only one event can be processed at a time.
|
||||
*/
|
||||
static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void dmeventd_lvm2_lock(void)
|
||||
{
|
||||
pthread_mutex_lock(&_event_mutex);
|
||||
}
|
||||
|
||||
void dmeventd_lvm2_unlock(void)
|
||||
{
|
||||
pthread_mutex_unlock(&_event_mutex);
|
||||
}
|
||||
|
||||
int dmeventd_lvm2_init(void)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
pthread_mutex_lock(&_register_mutex);
|
||||
|
||||
if (!_lvm_handle) {
|
||||
lvm2_log_fn(_lvm2_print_log);
|
||||
|
||||
if (!(_lvm_handle = lvm2_init_threaded()))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Need some space for allocations. 1024 should be more
|
||||
* than enough for what we need (device mapper name splitting)
|
||||
*/
|
||||
if (!_mem_pool && !(_mem_pool = dm_pool_create("mirror_dso", 1024))) {
|
||||
lvm2_exit(_lvm_handle);
|
||||
_lvm_handle = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
lvm2_disable_dmeventd_monitoring(_lvm_handle);
|
||||
/* FIXME Temporary: move to dmeventd core */
|
||||
lvm2_run(_lvm_handle, "_memlock_inc");
|
||||
log_debug("lvm plugin initialized.");
|
||||
}
|
||||
|
||||
_register_count++;
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&_register_mutex);
|
||||
return r;
|
||||
}
|
||||
|
||||
void dmeventd_lvm2_exit(void)
|
||||
{
|
||||
pthread_mutex_lock(&_register_mutex);
|
||||
|
||||
if (!--_register_count) {
|
||||
log_debug("lvm plugin shutting down.");
|
||||
lvm2_run(_lvm_handle, "_memlock_dec");
|
||||
dm_pool_destroy(_mem_pool);
|
||||
_mem_pool = NULL;
|
||||
dm_list_init(&_env_registry);
|
||||
lvm2_exit(_lvm_handle);
|
||||
_lvm_handle = NULL;
|
||||
log_debug("lvm plugin exited.");
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_register_mutex);
|
||||
}
|
||||
|
||||
struct dm_pool *dmeventd_lvm2_pool(void)
|
||||
{
|
||||
return _mem_pool;
|
||||
}
|
||||
|
||||
int dmeventd_lvm2_run(const char *cmdline)
|
||||
{
|
||||
/* coverity[missing_lock] no locking for run part */
|
||||
return (lvm2_run(_lvm_handle, cmdline) == LVM2_COMMAND_SUCCEEDED);
|
||||
}
|
||||
|
||||
int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
||||
const char *cmd, const char *device)
|
||||
{
|
||||
static char _internal_prefix[] = "_dmeventd_";
|
||||
char *vg = NULL, *lv = NULL, *layer;
|
||||
int r;
|
||||
struct env_data *env_data;
|
||||
const char *env = NULL;
|
||||
|
||||
if (!dm_split_lvm_name(mem, device, &vg, &lv, &layer)) {
|
||||
log_error("Unable to determine VG name from %s.",
|
||||
device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* strip off the mirror component designations */
|
||||
if ((layer = strstr(lv, "_mimagetmp")) ||
|
||||
(layer = strstr(lv, "_mlog")))
|
||||
*layer = '\0';
|
||||
|
||||
if (!strncmp(cmd, _internal_prefix, sizeof(_internal_prefix) - 1)) {
|
||||
/* check if ENVVAR wasn't already resolved */
|
||||
dm_list_iterate_items(env_data, &_env_registry)
|
||||
if (!strcmp(cmd, env_data->cmd)) {
|
||||
env = env_data->data;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!env) {
|
||||
/* run lvm2 command to find out setting value */
|
||||
dmeventd_lvm2_lock();
|
||||
if (!dmeventd_lvm2_run(cmd) ||
|
||||
!(env = getenv(cmd))) {
|
||||
dmeventd_lvm2_unlock();
|
||||
log_error("Unable to find configured command.");
|
||||
return 0;
|
||||
}
|
||||
/* output of internal command passed via env var */
|
||||
env = dm_pool_strdup(_mem_pool, env); /* copy with lock */
|
||||
dmeventd_lvm2_unlock();
|
||||
if (!env ||
|
||||
!(env_data = dm_pool_zalloc(_mem_pool, sizeof(*env_data))) ||
|
||||
!(env_data->cmd = dm_pool_strdup(_mem_pool, cmd))) {
|
||||
log_error("Unable to allocate env memory.");
|
||||
return 0;
|
||||
}
|
||||
env_data->data = env;
|
||||
/* add to ENVVAR registry */
|
||||
dm_list_add(&_env_registry, &env_data->list);
|
||||
}
|
||||
cmd = env;
|
||||
}
|
||||
|
||||
r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv);
|
||||
|
||||
dm_pool_free(mem, vg);
|
||||
|
||||
if (r < 0) {
|
||||
log_error("Unable to form LVM command. (too long).");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
76
daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h
Normal file
76
daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Wrappers around liblvm2cmd functions for dmeventd plug-ins.
|
||||
*
|
||||
* liblvm2cmd is not thread-safe so the locking in this library helps dmeventd
|
||||
* threads to co-operate in sharing a single instance.
|
||||
*
|
||||
* FIXME Either support this properly as a generic liblvm2cmd wrapper or make
|
||||
* liblvm2cmd thread-safe so this can go away.
|
||||
*/
|
||||
|
||||
#ifndef _DMEVENTD_LVMWRAP_H
|
||||
#define _DMEVENTD_LVMWRAP_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct dm_pool;
|
||||
|
||||
int dmeventd_lvm2_init(void);
|
||||
void dmeventd_lvm2_exit(void);
|
||||
int dmeventd_lvm2_run(const char *cmdline);
|
||||
|
||||
void dmeventd_lvm2_lock(void);
|
||||
void dmeventd_lvm2_unlock(void);
|
||||
|
||||
struct dm_pool *dmeventd_lvm2_pool(void);
|
||||
|
||||
int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
||||
const char *cmd, const char *device);
|
||||
|
||||
#define dmeventd_lvm2_run_with_lock(cmdline) \
|
||||
({\
|
||||
int rc;\
|
||||
dmeventd_lvm2_lock();\
|
||||
rc = dmeventd_lvm2_run(cmdline);\
|
||||
dmeventd_lvm2_unlock();\
|
||||
rc;\
|
||||
})
|
||||
|
||||
#define dmeventd_lvm2_init_with_pool(name, st) \
|
||||
({\
|
||||
struct dm_pool *mem;\
|
||||
st = NULL;\
|
||||
if (dmeventd_lvm2_init()) {\
|
||||
if ((mem = dm_pool_create(name, 2048)) &&\
|
||||
(st = dm_pool_zalloc(mem, sizeof(*st))))\
|
||||
st->mem = mem;\
|
||||
else {\
|
||||
if (mem)\
|
||||
dm_pool_destroy(mem);\
|
||||
dmeventd_lvm2_exit();\
|
||||
}\
|
||||
}\
|
||||
st;\
|
||||
})
|
||||
|
||||
#define dmeventd_lvm2_exit_with_pool(pool) \
|
||||
do {\
|
||||
dm_pool_destroy(pool->mem);\
|
||||
dmeventd_lvm2_exit();\
|
||||
} while(0)
|
||||
|
||||
#endif /* _DMEVENTD_LVMWRAP_H */
|
3
daemons/dmeventd/plugins/mirror/.exported_symbols
Normal file
3
daemons/dmeventd/plugins/mirror/.exported_symbols
Normal file
@ -0,0 +1,3 @@
|
||||
process_event
|
||||
register_device
|
||||
unregister_device
|
32
daemons/dmeventd/plugins/mirror/Makefile.in
Normal file
32
daemons/dmeventd/plugins/mirror/Makefile.in
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2005, 2008-2014 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
||||
LIBS += -ldevmapper-event-lvm2
|
||||
|
||||
SOURCES = dmeventd_mirror.c
|
||||
|
||||
LIB_NAME = libdevmapper-event-lvm2mirror
|
||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
install_lvm2: install_dm_plugin
|
||||
|
||||
install: install_lvm2
|
211
daemons/dmeventd/plugins/mirror/dmeventd_mirror.c
Normal file
211
daemons/dmeventd/plugins/mirror/dmeventd_mirror.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib/misc/lib.h"
|
||||
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
|
||||
#include "daemons/dmeventd/libdevmapper-event.h"
|
||||
#include "lib/activate/activate.h"
|
||||
|
||||
/* FIXME Reformat to 80 char lines. */
|
||||
|
||||
#define ME_IGNORE 0
|
||||
#define ME_INSYNC 1
|
||||
#define ME_FAILURE 2
|
||||
|
||||
struct dso_state {
|
||||
struct dm_pool *mem;
|
||||
char cmd_lvconvert[512];
|
||||
};
|
||||
|
||||
DM_EVENT_LOG_FN("mirr")
|
||||
|
||||
static void _process_status_code(dm_status_mirror_health_t health,
|
||||
uint32_t major, uint32_t minor,
|
||||
const char *dev_type, int *r)
|
||||
{
|
||||
/*
|
||||
* A => Alive - No failures
|
||||
* D => Dead - A write failure occurred leaving mirror out-of-sync
|
||||
* F => Flush failed.
|
||||
* S => Sync - A synchronization failure occurred, mirror out-of-sync
|
||||
* R => Read - A read failure occurred, mirror data unaffected
|
||||
* U => Unclassified failure (bug)
|
||||
*/
|
||||
switch (health) {
|
||||
case DM_STATUS_MIRROR_ALIVE:
|
||||
return;
|
||||
case DM_STATUS_MIRROR_FLUSH_FAILED:
|
||||
log_error("%s device %u:%u flush failed.",
|
||||
dev_type, major, minor);
|
||||
*r = ME_FAILURE;
|
||||
break;
|
||||
case DM_STATUS_MIRROR_SYNC_FAILED:
|
||||
log_error("%s device %u:%u sync failed.",
|
||||
dev_type, major, minor);
|
||||
break;
|
||||
case DM_STATUS_MIRROR_READ_FAILED:
|
||||
log_error("%s device %u:%u read failed.",
|
||||
dev_type, major, minor);
|
||||
break;
|
||||
default:
|
||||
log_error("%s device %u:%u has failed (%c).",
|
||||
dev_type, major, minor, (char)health);
|
||||
*r = ME_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int _get_mirror_event(struct dso_state *state, char *params)
|
||||
{
|
||||
int r = ME_INSYNC;
|
||||
unsigned i;
|
||||
struct dm_status_mirror *ms;
|
||||
|
||||
if (!dm_get_status_mirror(state->mem, params, &ms)) {
|
||||
log_error("Unable to parse mirror status string.");
|
||||
return ME_IGNORE;
|
||||
}
|
||||
|
||||
/* Check for bad mirror devices */
|
||||
for (i = 0; i < ms->dev_count; ++i)
|
||||
_process_status_code(ms->devs[i].health,
|
||||
ms->devs[i].major, ms->devs[i].minor,
|
||||
i ? "Secondary mirror" : "Primary mirror", &r);
|
||||
|
||||
/* Check for bad disk log device */
|
||||
for (i = 0; i < ms->log_count; ++i)
|
||||
_process_status_code(ms->logs[i].health,
|
||||
ms->logs[i].major, ms->logs[i].minor,
|
||||
"Log", &r);
|
||||
|
||||
/* Ignore if not in-sync */
|
||||
if ((r == ME_INSYNC) && (ms->insync_regions != ms->total_regions))
|
||||
r = ME_IGNORE;
|
||||
|
||||
dm_pool_free(state->mem, ms);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _remove_failed_devices(const char *cmd_lvconvert, const char *device)
|
||||
{
|
||||
/* if repair goes OK, report success even if lvscan has failed */
|
||||
if (!dmeventd_lvm2_run_with_lock(cmd_lvconvert)) {
|
||||
log_error("Repair of mirrored device %s failed.", device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_info("Repair of mirrored device %s finished successfully.", device);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt,
|
||||
enum dm_event_mask evmask __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
void *next = NULL;
|
||||
uint64_t start, length;
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
const char *device = dm_task_get_name(dmt);
|
||||
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &start, &length,
|
||||
&target_type, ¶ms);
|
||||
|
||||
if (!target_type) {
|
||||
log_info("%s mapping lost.", device);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(target_type, TARGET_NAME_MIRROR)) {
|
||||
log_info("%s has unmirrored portion.", device);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(_get_mirror_event(state, params)) {
|
||||
case ME_INSYNC:
|
||||
/* FIXME: all we really know is that this
|
||||
_part_ of the device is in sync
|
||||
Also, this is not an error
|
||||
*/
|
||||
log_notice("%s is now in-sync.", device);
|
||||
break;
|
||||
case ME_FAILURE:
|
||||
log_error("Device failure in %s.", device);
|
||||
if (!_remove_failed_devices(state->cmd_lvconvert, device))
|
||||
/* FIXME Why are all the error return codes unused? Get rid of them? */
|
||||
log_error("Failed to remove faulty devices in %s.",
|
||||
device);
|
||||
/* Should check before warning user that device is now linear
|
||||
else
|
||||
log_notice("%s is now a linear device.",
|
||||
device);
|
||||
*/
|
||||
break;
|
||||
case ME_IGNORE:
|
||||
break;
|
||||
default:
|
||||
/* FIXME Provide value then! */
|
||||
log_warn("WARNING: %s received unknown event.", device);
|
||||
}
|
||||
} while (next);
|
||||
}
|
||||
|
||||
int register_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state;
|
||||
|
||||
if (!dmeventd_lvm2_init_with_pool("mirror_state", state))
|
||||
goto_bad;
|
||||
|
||||
/* CANNOT use --config as this disables cached content */
|
||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
|
||||
"lvconvert --repair --use-policies", device))
|
||||
goto_bad;
|
||||
|
||||
*user = state;
|
||||
|
||||
log_info("Monitoring mirror device %s for events.", device);
|
||||
|
||||
return 1;
|
||||
bad:
|
||||
log_error("Failed to monitor mirror %s.", device);
|
||||
|
||||
if (state)
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unregister_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
log_info("No longer monitoring mirror device %s for events.",
|
||||
device);
|
||||
|
||||
return 1;
|
||||
}
|
3
daemons/dmeventd/plugins/raid/.exported_symbols
Normal file
3
daemons/dmeventd/plugins/raid/.exported_symbols
Normal file
@ -0,0 +1,3 @@
|
||||
process_event
|
||||
register_device
|
||||
unregister_device
|
31
daemons/dmeventd/plugins/raid/Makefile.in
Normal file
31
daemons/dmeventd/plugins/raid/Makefile.in
Normal file
@ -0,0 +1,31 @@
|
||||
#
|
||||
# Copyright (C) 2011-2014 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
||||
LIBS += -ldevmapper-event-lvm2
|
||||
|
||||
SOURCES = dmeventd_raid.c
|
||||
|
||||
LIB_NAME = libdevmapper-event-lvm2raid
|
||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
install_lvm2: install_dm_plugin
|
||||
|
||||
install: install_lvm2
|
189
daemons/dmeventd/plugins/raid/dmeventd_raid.c
Normal file
189
daemons/dmeventd/plugins/raid/dmeventd_raid.c
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib/misc/lib.h"
|
||||
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
|
||||
#include "daemons/dmeventd/libdevmapper-event.h"
|
||||
#include "lib/config/defaults.h"
|
||||
|
||||
/* Hold enough elements for the maximum number of RAID images */
|
||||
#define RAID_DEVS_ELEMS ((DEFAULT_RAID_MAX_IMAGES + 63) / 64)
|
||||
|
||||
struct dso_state {
|
||||
struct dm_pool *mem;
|
||||
char cmd_lvconvert[512];
|
||||
uint64_t raid_devs[RAID_DEVS_ELEMS];
|
||||
int failed;
|
||||
int warned;
|
||||
};
|
||||
|
||||
DM_EVENT_LOG_FN("raid")
|
||||
|
||||
/* FIXME Reformat to 80 char lines. */
|
||||
|
||||
static int _process_raid_event(struct dso_state *state, char *params, const char *device)
|
||||
{
|
||||
struct dm_status_raid *status;
|
||||
const char *d;
|
||||
int dead = 0, r = 1;
|
||||
uint32_t dev;
|
||||
|
||||
if (!dm_get_status_raid(state->mem, params, &status)) {
|
||||
log_error("Failed to process status line for %s.", device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
d = status->dev_health;
|
||||
while ((d = strchr(d, 'D'))) {
|
||||
dev = (uint32_t)(d - status->dev_health);
|
||||
|
||||
if (!(state->raid_devs[dev / 64] & (UINT64_C(1) << (dev % 64)))) {
|
||||
state->raid_devs[dev / 64] |= (UINT64_C(1) << (dev % 64));
|
||||
log_warn("WARNING: Device #%u of %s array, %s, has failed.",
|
||||
dev, status->raid_type, device);
|
||||
}
|
||||
|
||||
d++;
|
||||
dead = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we are converting from non-RAID to RAID (e.g. linear -> raid1)
|
||||
* and too many original devices die, such that we cannot continue
|
||||
* the "recover" operation, the sync action will go to "idle", the
|
||||
* unsynced devs will remain at 'a', and the original devices will
|
||||
* NOT SWITCH TO 'D', but will remain at 'A' - hoping to be revived.
|
||||
*
|
||||
* This is simply the way the kernel works...
|
||||
*/
|
||||
if (!strcmp(status->sync_action, "idle") &&
|
||||
(status->dev_health[0] == 'a') &&
|
||||
(status->insync_regions < status->total_regions)) {
|
||||
log_error("Primary sources for new RAID, %s, have failed.",
|
||||
device);
|
||||
dead = 1; /* run it through LVM repair */
|
||||
}
|
||||
|
||||
if (dead) {
|
||||
/*
|
||||
* Use the first event to run a repair ignoring any additional ones.
|
||||
*
|
||||
* We presume lvconvert to do pre-repair
|
||||
* checks to avoid bloat in this plugin.
|
||||
*/
|
||||
if (!state->warned && status->insync_regions < status->total_regions) {
|
||||
state->warned = 1;
|
||||
log_warn("WARNING: waiting for resynchronization to finish "
|
||||
"before initiating repair on RAID device %s.", device);
|
||||
/* Fall through to allow lvconvert to run. */
|
||||
}
|
||||
|
||||
if (state->failed)
|
||||
goto out; /* already reported */
|
||||
|
||||
state->failed = 1;
|
||||
|
||||
/* if repair goes OK, report success even if lvscan has failed */
|
||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) {
|
||||
log_error("Repair of RAID device %s failed.", device);
|
||||
r = 0;
|
||||
}
|
||||
} else {
|
||||
state->failed = 0;
|
||||
if (status->insync_regions == status->total_regions)
|
||||
memset(&state->raid_devs, 0, sizeof(state->raid_devs));
|
||||
log_info("%s array, %s, is %s in-sync.",
|
||||
status->raid_type, device,
|
||||
(status->insync_regions == status->total_regions) ? "now" : "not");
|
||||
}
|
||||
out:
|
||||
dm_pool_free(state->mem, status);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt,
|
||||
enum dm_event_mask evmask __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
void *next = NULL;
|
||||
uint64_t start, length;
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
const char *device = dm_task_get_name(dmt);
|
||||
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &start, &length,
|
||||
&target_type, ¶ms);
|
||||
|
||||
if (!target_type) {
|
||||
log_info("%s mapping lost.", device);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(target_type, "raid")) {
|
||||
log_info("%s has non-raid portion.", device);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_process_raid_event(state, params, device))
|
||||
log_error("Failed to process event for %s.",
|
||||
device);
|
||||
} while (next);
|
||||
}
|
||||
|
||||
int register_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state;
|
||||
|
||||
if (!dmeventd_lvm2_init_with_pool("raid_state", state))
|
||||
goto_bad;
|
||||
|
||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
|
||||
"lvconvert --repair --use-policies", device))
|
||||
goto_bad;
|
||||
|
||||
*user = state;
|
||||
|
||||
log_info("Monitoring RAID device %s for events.", device);
|
||||
|
||||
return 1;
|
||||
bad:
|
||||
log_error("Failed to monitor RAID %s.", device);
|
||||
|
||||
if (state)
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unregister_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
log_info("No longer monitoring RAID device %s for events.",
|
||||
device);
|
||||
|
||||
return 1;
|
||||
}
|
3
daemons/dmeventd/plugins/snapshot/.exported_symbols
Normal file
3
daemons/dmeventd/plugins/snapshot/.exported_symbols
Normal file
@ -0,0 +1,3 @@
|
||||
process_event
|
||||
register_device
|
||||
unregister_device
|
31
daemons/dmeventd/plugins/snapshot/Makefile.in
Normal file
31
daemons/dmeventd/plugins/snapshot/Makefile.in
Normal file
@ -0,0 +1,31 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of the LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
||||
LIBS += -ldevmapper-event-lvm2
|
||||
|
||||
SOURCES = dmeventd_snapshot.c
|
||||
|
||||
LIB_SHARED = libdevmapper-event-lvm2snapshot.$(LIB_SUFFIX)
|
||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
install_lvm2: install_dm_plugin
|
||||
|
||||
install: install_lvm2
|
290
daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
Normal file
290
daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2015 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib/misc/lib.h"
|
||||
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
|
||||
#include "daemons/dmeventd/libdevmapper-event.h"
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* First warning when snapshot is 80% full. */
|
||||
#define WARNING_THRESH (DM_PERCENT_1 * 80)
|
||||
/* Run a check every 5%. */
|
||||
#define CHECK_STEP (DM_PERCENT_1 * 5)
|
||||
/* Do not bother checking snapshots less than 50% full. */
|
||||
#define CHECK_MINIMUM (DM_PERCENT_1 * 50)
|
||||
|
||||
#define UMOUNT_COMMAND "/bin/umount"
|
||||
|
||||
struct dso_state {
|
||||
struct dm_pool *mem;
|
||||
dm_percent_t percent_check;
|
||||
uint64_t known_size;
|
||||
char cmd_lvextend[512];
|
||||
};
|
||||
|
||||
DM_EVENT_LOG_FN("snap")
|
||||
|
||||
static int _run(const char *cmd, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int argc = 1; /* for argv[0], i.e. cmd */
|
||||
int i = 0;
|
||||
const char **argv;
|
||||
pid_t pid = fork();
|
||||
int status;
|
||||
|
||||
if (pid == 0) { /* child */
|
||||
va_start(ap, cmd);
|
||||
while (va_arg(ap, const char *))
|
||||
++ argc;
|
||||
va_end(ap);
|
||||
|
||||
/* + 1 for the terminating NULL */
|
||||
argv = alloca(sizeof(const char *) * (argc + 1));
|
||||
|
||||
argv[0] = cmd;
|
||||
va_start(ap, cmd);
|
||||
while ((argv[++i] = va_arg(ap, const char *)));
|
||||
va_end(ap);
|
||||
|
||||
execvp(cmd, (char **)argv);
|
||||
log_sys_error("exec", cmd);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
if (pid > 0) { /* parent */
|
||||
if (waitpid(pid, &status, 0) != pid)
|
||||
return 0; /* waitpid failed */
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status))
|
||||
return 0; /* the child failed */
|
||||
}
|
||||
|
||||
if (pid < 0)
|
||||
return 0; /* fork failed */
|
||||
|
||||
return 1; /* all good */
|
||||
}
|
||||
|
||||
static int _extend(const char *cmd)
|
||||
{
|
||||
log_debug("Extending snapshot via %s.", cmd);
|
||||
return dmeventd_lvm2_run_with_lock(cmd);
|
||||
}
|
||||
|
||||
#ifdef SNAPSHOT_REMOVE
|
||||
/* Remove invalid snapshot from dm-table */
|
||||
/* Experimental for now and not used by default */
|
||||
static int _remove(const char *uuid)
|
||||
{
|
||||
int r = 1;
|
||||
uint32_t cookie = 0;
|
||||
struct dm_task *dmt;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
|
||||
return 0;
|
||||
|
||||
if (!dm_task_set_uuid(dmt, uuid)) {
|
||||
r = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
dm_task_retry_remove(dmt);
|
||||
|
||||
if (!dm_task_set_cookie(dmt, &cookie, 0)) {
|
||||
r = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
r = 0;
|
||||
goto_out;
|
||||
}
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif /* SNAPSHOT_REMOVE */
|
||||
|
||||
static void _umount(const char *device, int major, int minor)
|
||||
{
|
||||
FILE *mounts;
|
||||
char buffer[4096];
|
||||
char *words[3];
|
||||
struct stat st;
|
||||
const char procmounts[] = "/proc/mounts";
|
||||
|
||||
if (!(mounts = fopen(procmounts, "r"))) {
|
||||
log_sys_error("fopen", procmounts);
|
||||
log_error("Not umounting %s.", device);
|
||||
return;
|
||||
}
|
||||
|
||||
while (!feof(mounts)) {
|
||||
/* read a line of /proc/mounts */
|
||||
if (!fgets(buffer, sizeof(buffer), mounts))
|
||||
break; /* eof, likely */
|
||||
|
||||
/* words[0] is the mount point and words[1] is the device path */
|
||||
if (dm_split_words(buffer, 3, 0, words) < 2)
|
||||
continue;
|
||||
|
||||
/* find the major/minor of the device */
|
||||
if (stat(words[0], &st))
|
||||
continue; /* can't stat, skip this one */
|
||||
|
||||
if (S_ISBLK(st.st_mode) &&
|
||||
(int) major(st.st_rdev) == major &&
|
||||
(int) minor(st.st_rdev) == minor) {
|
||||
log_error("Unmounting invalid snapshot %s from %s.", device, words[1]);
|
||||
if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
|
||||
log_error("Failed to umount snapshot %s from %s: %s.",
|
||||
device, words[1], strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (fclose(mounts))
|
||||
log_sys_error("close", procmounts);
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt,
|
||||
enum dm_event_mask evmask __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
void *next = NULL;
|
||||
uint64_t start, length;
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
struct dm_status_snapshot *status = NULL;
|
||||
const char *device = dm_task_get_name(dmt);
|
||||
int percent;
|
||||
struct dm_info info;
|
||||
int ret;
|
||||
|
||||
/* No longer monitoring, waiting for remove */
|
||||
if (!state->percent_check)
|
||||
return;
|
||||
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
if (!target_type || strcmp(target_type, "snapshot")) {
|
||||
log_error("Target %s is not snapshot.", target_type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dm_get_status_snapshot(state->mem, params, &status)) {
|
||||
log_error("Cannot parse snapshot %s state: %s.", device, params);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the snapshot has been invalidated or we failed to parse
|
||||
* the status string. Report the full status string to syslog.
|
||||
*/
|
||||
if (status->invalid || status->overflow || !status->total_sectors) {
|
||||
log_warn("WARNING: Snapshot %s changed state to: %s and should be removed.",
|
||||
device, params);
|
||||
state->percent_check = 0;
|
||||
if (dm_task_get_info(dmt, &info))
|
||||
_umount(device, info.major, info.minor);
|
||||
#ifdef SNAPSHOT_REMOVE
|
||||
/* Maybe configurable ? */
|
||||
_remove(dm_task_get_uuid(dmt));
|
||||
#endif
|
||||
if ((ret = pthread_kill(pthread_self(), SIGALRM)) && (ret != ESRCH))
|
||||
log_sys_error("pthread_kill", "self");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (length <= (status->used_sectors - status->metadata_sectors)) {
|
||||
/* TODO eventually recognize earlier when room is enough */
|
||||
log_info("Dropping monitoring of fully provisioned snapshot %s.",
|
||||
device);
|
||||
if ((ret = pthread_kill(pthread_self(), SIGALRM)) && (ret != ESRCH))
|
||||
log_sys_error("pthread_kill", "self");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Snapshot size had changed. Clear the threshold. */
|
||||
if (state->known_size != status->total_sectors) {
|
||||
state->percent_check = CHECK_MINIMUM;
|
||||
state->known_size = status->total_sectors;
|
||||
}
|
||||
|
||||
percent = dm_make_percent(status->used_sectors, status->total_sectors);
|
||||
if (percent >= state->percent_check) {
|
||||
/* Usage has raised more than CHECK_STEP since the last
|
||||
time. Run actions. */
|
||||
state->percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
|
||||
|
||||
if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
|
||||
log_warn("WARNING: Snapshot %s is now %.2f%% full.",
|
||||
device, dm_percent_to_round_float(percent, 2));
|
||||
|
||||
/* Try to extend the snapshot, in accord with user-set policies */
|
||||
if (!_extend(state->cmd_lvextend))
|
||||
log_error("Failed to extend snapshot %s.", device);
|
||||
}
|
||||
out:
|
||||
dm_pool_free(state->mem, status);
|
||||
}
|
||||
|
||||
int register_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state;
|
||||
|
||||
if (!dmeventd_lvm2_init_with_pool("snapshot_state", state))
|
||||
goto_bad;
|
||||
|
||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvextend,
|
||||
sizeof(state->cmd_lvextend),
|
||||
"lvextend --use-policies", device))
|
||||
goto_bad;
|
||||
|
||||
state->percent_check = CHECK_MINIMUM;
|
||||
*user = state;
|
||||
|
||||
log_info("Monitoring snapshot %s.", device);
|
||||
|
||||
return 1;
|
||||
bad:
|
||||
log_error("Failed to monitor snapshot %s.", device);
|
||||
|
||||
if (state)
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unregister_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
log_info("No longer monitoring snapshot %s.", device);
|
||||
|
||||
return 1;
|
||||
}
|
3
daemons/dmeventd/plugins/thin/.exported_symbols
Normal file
3
daemons/dmeventd/plugins/thin/.exported_symbols
Normal file
@ -0,0 +1,3 @@
|
||||
process_event
|
||||
register_device
|
||||
unregister_device
|
31
daemons/dmeventd/plugins/thin/Makefile.in
Normal file
31
daemons/dmeventd/plugins/thin/Makefile.in
Normal file
@ -0,0 +1,31 @@
|
||||
#
|
||||
# Copyright (C) 2011-2014 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
||||
LIBS += -ldevmapper-event-lvm2
|
||||
|
||||
SOURCES = dmeventd_thin.c
|
||||
|
||||
LIB_NAME = libdevmapper-event-lvm2thin
|
||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
install_lvm2: install_dm_plugin
|
||||
|
||||
install: install_lvm2
|
436
daemons/dmeventd/plugins/thin/dmeventd_thin.c
Normal file
436
daemons/dmeventd/plugins/thin/dmeventd_thin.c
Normal file
@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2017 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib/misc/lib.h"
|
||||
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
|
||||
#include "daemons/dmeventd/libdevmapper-event.h"
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* TODO - move this mountinfo code into library to be reusable */
|
||||
#ifdef __linux__
|
||||
# include "libdm/misc/kdev_t.h"
|
||||
#else
|
||||
# define MAJOR(x) major((x))
|
||||
# define MINOR(x) minor((x))
|
||||
#endif
|
||||
|
||||
/* First warning when thin data or metadata is 80% full. */
|
||||
#define WARNING_THRESH (DM_PERCENT_1 * 80)
|
||||
/* Umount thin LVs when thin data or metadata LV is >=
|
||||
* and lvextend --use-policies has failed. */
|
||||
#define UMOUNT_THRESH (DM_PERCENT_1 * 95)
|
||||
/* Run a check every 5%. */
|
||||
#define CHECK_STEP (DM_PERCENT_1 * 5)
|
||||
/* Do not bother checking thin data or metadata is less than 50% full. */
|
||||
#define CHECK_MINIMUM (DM_PERCENT_1 * 50)
|
||||
|
||||
#define UMOUNT_COMMAND "/bin/umount"
|
||||
|
||||
#define MAX_FAILS (256) /* ~42 mins between cmd call retry with 10s delay */
|
||||
|
||||
#define THIN_DEBUG 0
|
||||
|
||||
struct dso_state {
|
||||
struct dm_pool *mem;
|
||||
int metadata_percent_check;
|
||||
int metadata_percent;
|
||||
int data_percent_check;
|
||||
int data_percent;
|
||||
uint64_t known_metadata_size;
|
||||
uint64_t known_data_size;
|
||||
unsigned fails;
|
||||
unsigned max_fails;
|
||||
int restore_sigset;
|
||||
sigset_t old_sigset;
|
||||
pid_t pid;
|
||||
char *argv[3];
|
||||
char *cmd_str;
|
||||
};
|
||||
|
||||
DM_EVENT_LOG_FN("thin")
|
||||
|
||||
static int _run_command(struct dso_state *state)
|
||||
{
|
||||
char val[16];
|
||||
int i;
|
||||
|
||||
/* Mark for possible lvm2 command we are running from dmeventd
|
||||
* lvm2 will not try to talk back to dmeventd while processing it */
|
||||
(void) setenv("LVM_RUN_BY_DMEVENTD", "1", 1);
|
||||
|
||||
if (state->data_percent) {
|
||||
/* Prepare some known data to env vars for easy use */
|
||||
if (dm_snprintf(val, sizeof(val), "%d",
|
||||
state->data_percent / DM_PERCENT_1) != -1)
|
||||
(void) setenv("DMEVENTD_THIN_POOL_DATA", val, 1);
|
||||
if (dm_snprintf(val, sizeof(val), "%d",
|
||||
state->metadata_percent / DM_PERCENT_1) != -1)
|
||||
(void) setenv("DMEVENTD_THIN_POOL_METADATA", val, 1);
|
||||
} else {
|
||||
/* For an error event it's for a user to check status and decide */
|
||||
log_debug("Error event processing.");
|
||||
}
|
||||
|
||||
log_verbose("Executing command: %s", state->cmd_str);
|
||||
|
||||
/* TODO:
|
||||
* Support parallel run of 'task' and it's waitpid maintenance
|
||||
* ATM we can't handle signaling of SIGALRM
|
||||
* as signalling is not allowed while 'process_event()' is running
|
||||
*/
|
||||
if (!(state->pid = fork())) {
|
||||
/* child */
|
||||
(void) close(0);
|
||||
for (i = 3; i < 255; ++i) (void) close(i);
|
||||
execvp(state->argv[0], state->argv);
|
||||
_exit(errno);
|
||||
} else if (state->pid == -1) {
|
||||
log_error("Can't fork command %s.", state->cmd_str);
|
||||
state->fails = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
|
||||
{
|
||||
#if THIN_DEBUG
|
||||
log_debug("dmeventd executes: %s.", state->cmd_str);
|
||||
#endif
|
||||
if (state->argv[0])
|
||||
return _run_command(state);
|
||||
|
||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) {
|
||||
log_error("Failed command for %s.", dm_task_get_name(dmt));
|
||||
state->fails = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->fails = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check if executed command has finished
|
||||
* Only 1 command may run */
|
||||
static int _wait_for_pid(struct dso_state *state)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (state->pid == -1)
|
||||
return 1;
|
||||
|
||||
if (!waitpid(state->pid, &status, WNOHANG))
|
||||
return 0;
|
||||
|
||||
/* Wait for finish */
|
||||
if (WIFEXITED(status)) {
|
||||
log_verbose("Child %d exited with status %d.",
|
||||
state->pid, WEXITSTATUS(status));
|
||||
state->fails = WEXITSTATUS(status) ? 1 : 0;
|
||||
} else {
|
||||
if (WIFSIGNALED(status))
|
||||
log_verbose("Child %d was terminated with status %d.",
|
||||
state->pid, WTERMSIG(status));
|
||||
state->fails = 1;
|
||||
}
|
||||
|
||||
state->pid = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt,
|
||||
enum dm_event_mask evmask,
|
||||
void **user)
|
||||
{
|
||||
const char *device = dm_task_get_name(dmt);
|
||||
struct dso_state *state = *user;
|
||||
struct dm_status_thin_pool *tps = NULL;
|
||||
void *next = NULL;
|
||||
uint64_t start, length;
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
int needs_policy = 0;
|
||||
struct dm_task *new_dmt = NULL;
|
||||
|
||||
#if THIN_DEBUG
|
||||
log_debug("Watch for tp-data:%.2f%% tp-metadata:%.2f%%.",
|
||||
dm_percent_to_round_float(state->data_percent_check, 2),
|
||||
dm_percent_to_round_float(state->metadata_percent_check, 2));
|
||||
#endif
|
||||
if (!_wait_for_pid(state)) {
|
||||
log_warn("WARNING: Skipping event, child %d is still running (%s).",
|
||||
state->pid, state->cmd_str);
|
||||
return;
|
||||
}
|
||||
|
||||
if (evmask & DM_EVENT_DEVICE_ERROR) {
|
||||
/* Error -> no need to check and do instant resize */
|
||||
state->data_percent = state->metadata_percent = 0;
|
||||
if (_use_policy(dmt, state))
|
||||
goto out;
|
||||
|
||||
stack;
|
||||
|
||||
/*
|
||||
* Rather update oldish status
|
||||
* since after 'command' processing
|
||||
* percentage info could have changed a lot.
|
||||
* If we would get above UMOUNT_THRESH
|
||||
* we would wait for next sigalarm.
|
||||
*/
|
||||
if (!(new_dmt = dm_task_create(DM_DEVICE_STATUS)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_set_uuid(new_dmt, dm_task_get_uuid(dmt)))
|
||||
goto_out;
|
||||
|
||||
/* Non-blocking status read */
|
||||
if (!dm_task_no_flush(new_dmt))
|
||||
log_warn("WARNING: Can't set no_flush for dm status.");
|
||||
|
||||
if (!dm_task_run(new_dmt))
|
||||
goto_out;
|
||||
|
||||
dmt = new_dmt;
|
||||
}
|
||||
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
|
||||
if (!target_type || (strcmp(target_type, "thin-pool") != 0)) {
|
||||
log_error("Invalid target type.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!dm_get_status_thin_pool(state->mem, params, &tps)) {
|
||||
log_error("Failed to parse status.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if THIN_DEBUG
|
||||
log_debug("Thin pool status " FMTu64 "/" FMTu64 " "
|
||||
FMTu64 "/" FMTu64 ".",
|
||||
tps->used_metadata_blocks, tps->total_metadata_blocks,
|
||||
tps->used_data_blocks, tps->total_data_blocks);
|
||||
#endif
|
||||
|
||||
/* Thin pool size had changed. Clear the threshold. */
|
||||
if (state->known_metadata_size != tps->total_metadata_blocks) {
|
||||
state->metadata_percent_check = CHECK_MINIMUM;
|
||||
state->known_metadata_size = tps->total_metadata_blocks;
|
||||
state->fails = 0;
|
||||
}
|
||||
|
||||
if (state->known_data_size != tps->total_data_blocks) {
|
||||
state->data_percent_check = CHECK_MINIMUM;
|
||||
state->known_data_size = tps->total_data_blocks;
|
||||
state->fails = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger action when threshold boundary is exceeded.
|
||||
* Report 80% threshold warning when it's used above 80%.
|
||||
* Only 100% is exception as it cannot be surpassed so policy
|
||||
* action is called for: >50%, >55% ... >95%, 100%
|
||||
*/
|
||||
state->metadata_percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks);
|
||||
if ((state->metadata_percent > WARNING_THRESH) &&
|
||||
(state->metadata_percent > state->metadata_percent_check))
|
||||
log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.",
|
||||
device, dm_percent_to_round_float(state->metadata_percent, 2));
|
||||
if (state->metadata_percent > CHECK_MINIMUM) {
|
||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
||||
if (state->metadata_percent > state->metadata_percent_check)
|
||||
needs_policy = 1;
|
||||
state->metadata_percent_check = (state->metadata_percent / CHECK_STEP + 1) * CHECK_STEP;
|
||||
if (state->metadata_percent_check == DM_PERCENT_100)
|
||||
state->metadata_percent_check--; /* Can't get bigger then 100% */
|
||||
} else
|
||||
state->metadata_percent_check = CHECK_MINIMUM;
|
||||
|
||||
state->data_percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks);
|
||||
if ((state->data_percent > WARNING_THRESH) &&
|
||||
(state->data_percent > state->data_percent_check))
|
||||
log_warn("WARNING: Thin pool %s data is now %.2f%% full.",
|
||||
device, dm_percent_to_round_float(state->data_percent, 2));
|
||||
if (state->data_percent > CHECK_MINIMUM) {
|
||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
||||
if (state->data_percent > state->data_percent_check)
|
||||
needs_policy = 1;
|
||||
state->data_percent_check = (state->data_percent / CHECK_STEP + 1) * CHECK_STEP;
|
||||
if (state->data_percent_check == DM_PERCENT_100)
|
||||
state->data_percent_check--; /* Can't get bigger then 100% */
|
||||
} else
|
||||
state->data_percent_check = CHECK_MINIMUM;
|
||||
|
||||
/* Reduce number of _use_policy() calls by power-of-2 factor till frequency of MAX_FAILS is reached.
|
||||
* Avoids too high number of error retries, yet shows some status messages in log regularly.
|
||||
* i.e. PV could have been pvmoved and VG/LV was locked for a while...
|
||||
*/
|
||||
if (state->fails) {
|
||||
if (state->fails++ <= state->max_fails) {
|
||||
log_debug("Postponing frequently failing policy (%u <= %u).",
|
||||
state->fails - 1, state->max_fails);
|
||||
goto out;
|
||||
}
|
||||
if (state->max_fails < MAX_FAILS)
|
||||
state->max_fails <<= 1;
|
||||
state->fails = needs_policy = 1; /* Retry failing command */
|
||||
} else
|
||||
state->max_fails = 1; /* Reset on success */
|
||||
|
||||
if (needs_policy)
|
||||
_use_policy(dmt, state);
|
||||
out:
|
||||
if (tps)
|
||||
dm_pool_free(state->mem, tps);
|
||||
|
||||
if (new_dmt)
|
||||
dm_task_destroy(new_dmt);
|
||||
}
|
||||
|
||||
/* Handle SIGCHLD for a thread */
|
||||
static void _sig_child(int signum __attribute__((unused)))
|
||||
{
|
||||
/* empty SIG_IGN */;
|
||||
}
|
||||
|
||||
/* Setup handler for SIGCHLD when executing external command
|
||||
* to get quick 'waitpid()' reaction
|
||||
* It will interrupt syscall just like SIGALRM and
|
||||
* invoke process_event().
|
||||
*/
|
||||
static void _init_thread_signals(struct dso_state *state)
|
||||
{
|
||||
struct sigaction act = { .sa_handler = _sig_child };
|
||||
sigset_t my_sigset;
|
||||
|
||||
sigemptyset(&my_sigset);
|
||||
|
||||
if (sigaction(SIGCHLD, &act, NULL))
|
||||
log_warn("WARNING: Failed to set SIGCHLD action.");
|
||||
else if (sigaddset(&my_sigset, SIGCHLD))
|
||||
log_warn("WARNING: Failed to add SIGCHLD to set.");
|
||||
else if (pthread_sigmask(SIG_UNBLOCK, &my_sigset, &state->old_sigset))
|
||||
log_warn("WARNING: Failed to unblock SIGCHLD.");
|
||||
else
|
||||
state->restore_sigset = 1;
|
||||
}
|
||||
|
||||
static void _restore_thread_signals(struct dso_state *state)
|
||||
{
|
||||
if (state->restore_sigset &&
|
||||
pthread_sigmask(SIG_SETMASK, &state->old_sigset, NULL))
|
||||
log_warn("WARNING: Failed to block SIGCHLD.");
|
||||
}
|
||||
|
||||
int register_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state;
|
||||
char *str;
|
||||
char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */
|
||||
|
||||
if (!dmeventd_lvm2_init_with_pool("thin_pool_state", state))
|
||||
goto_bad;
|
||||
|
||||
if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str),
|
||||
"_dmeventd_thin_command", device))
|
||||
goto_bad;
|
||||
|
||||
if (strncmp(cmd_str, "lvm ", 4) == 0) {
|
||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) {
|
||||
log_error("Failed to copy lvm command.");
|
||||
goto bad;
|
||||
}
|
||||
} else if (cmd_str[0] == '/') {
|
||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) {
|
||||
log_error("Failed to copy thin command.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* Find last space before 'vg/lv' */
|
||||
if (!(str = strrchr(state->cmd_str, ' ')))
|
||||
goto inval;
|
||||
|
||||
if (!(state->argv[0] = dm_pool_strndup(state->mem, state->cmd_str,
|
||||
str - state->cmd_str))) {
|
||||
log_error("Failed to copy command.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
state->argv[1] = str + 1; /* 1 argument - vg/lv */
|
||||
_init_thread_signals(state);
|
||||
} else /* Unsupported command format */
|
||||
goto inval;
|
||||
|
||||
state->pid = -1;
|
||||
*user = state;
|
||||
|
||||
log_info("Monitoring thin pool %s.", device);
|
||||
|
||||
return 1;
|
||||
inval:
|
||||
log_error("Invalid command for monitoring: %s.", cmd_str);
|
||||
bad:
|
||||
log_error("Failed to monitor thin pool %s.", device);
|
||||
|
||||
if (state)
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unregister_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
int i;
|
||||
|
||||
for (i = 0; !_wait_for_pid(state) && (i < 6); ++i) {
|
||||
if (i == 0)
|
||||
/* Give it 2 seconds, then try to terminate & kill it */
|
||||
log_verbose("Child %d still not finished (%s) waiting.",
|
||||
state->pid, state->cmd_str);
|
||||
else if (i == 3) {
|
||||
log_warn("WARNING: Terminating child %d.", state->pid);
|
||||
kill(state->pid, SIGINT);
|
||||
kill(state->pid, SIGTERM);
|
||||
} else if (i == 5) {
|
||||
log_warn("WARNING: Killing child %d.", state->pid);
|
||||
kill(state->pid, SIGKILL);
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (state->pid != -1)
|
||||
log_warn("WARNING: Cannot kill child %d!", state->pid);
|
||||
|
||||
_restore_thread_signals(state);
|
||||
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
log_info("No longer monitoring thin pool %s.", device);
|
||||
|
||||
return 1;
|
||||
}
|
3
daemons/dmeventd/plugins/vdo/.exported_symbols
Normal file
3
daemons/dmeventd/plugins/vdo/.exported_symbols
Normal file
@ -0,0 +1,3 @@
|
||||
process_event
|
||||
register_device
|
||||
unregister_device
|
31
daemons/dmeventd/plugins/vdo/Makefile.in
Normal file
31
daemons/dmeventd/plugins/vdo/Makefile.in
Normal file
@ -0,0 +1,31 @@
|
||||
#
|
||||
# Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
||||
LIBS += -ldevmapper-event-lvm2
|
||||
|
||||
SOURCES = dmeventd_vdo.c
|
||||
|
||||
LIB_NAME = libdevmapper-event-lvm2vdo
|
||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
install_lvm2: install_dm_plugin
|
||||
|
||||
install: install_lvm2
|
413
daemons/dmeventd/plugins/vdo/dmeventd_vdo.c
Normal file
413
daemons/dmeventd/plugins/vdo/dmeventd_vdo.c
Normal file
@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib/misc/lib.h"
|
||||
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
|
||||
#include "daemons/dmeventd/libdevmapper-event.h"
|
||||
|
||||
/*
|
||||
* Use parser from new device_mapper library.
|
||||
* Although during compilation we can see dm_vdo_status_parse()
|
||||
* in runtime we are linked against systems libdm 'older' library
|
||||
* which does not provide this symbol and plugin fails to load
|
||||
*/
|
||||
/* coverity[unnecessary_header] used for parsing */
|
||||
#include "device_mapper/vdo/status.c"
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* First warning when VDO pool is 80% full. */
|
||||
#define WARNING_THRESH (DM_PERCENT_1 * 80)
|
||||
/* Run a check every 5%. */
|
||||
#define CHECK_STEP (DM_PERCENT_1 * 5)
|
||||
/* Do not bother checking VDO pool is less than 50% full. */
|
||||
#define CHECK_MINIMUM (DM_PERCENT_1 * 50)
|
||||
|
||||
#define MAX_FAILS (256) /* ~42 mins between cmd call retry with 10s delay */
|
||||
|
||||
#define VDO_DEBUG 0
|
||||
|
||||
struct dso_state {
|
||||
struct dm_pool *mem;
|
||||
int percent_check;
|
||||
int percent;
|
||||
uint64_t known_data_size;
|
||||
unsigned fails;
|
||||
unsigned max_fails;
|
||||
int restore_sigset;
|
||||
sigset_t old_sigset;
|
||||
pid_t pid;
|
||||
char *argv[3];
|
||||
const char *cmd_str;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
DM_EVENT_LOG_FN("vdo")
|
||||
|
||||
static int _run_command(struct dso_state *state)
|
||||
{
|
||||
char val[16];
|
||||
int i;
|
||||
|
||||
/* Mark for possible lvm2 command we are running from dmeventd
|
||||
* lvm2 will not try to talk back to dmeventd while processing it */
|
||||
(void) setenv("LVM_RUN_BY_DMEVENTD", "1", 1);
|
||||
|
||||
if (state->percent) {
|
||||
/* Prepare some known data to env vars for easy use */
|
||||
if (dm_snprintf(val, sizeof(val), "%d",
|
||||
state->percent / DM_PERCENT_1) != -1)
|
||||
(void) setenv("DMEVENTD_VDO_POOL", val, 1);
|
||||
} else {
|
||||
/* For an error event it's for a user to check status and decide */
|
||||
log_debug("Error event processing.");
|
||||
}
|
||||
|
||||
log_verbose("Executing command: %s", state->cmd_str);
|
||||
|
||||
/* TODO:
|
||||
* Support parallel run of 'task' and it's waitpid maintenance
|
||||
* ATM we can't handle signaling of SIGALRM
|
||||
* as signalling is not allowed while 'process_event()' is running
|
||||
*/
|
||||
if (!(state->pid = fork())) {
|
||||
/* child */
|
||||
(void) close(0);
|
||||
for (i = 3; i < 255; ++i) (void) close(i);
|
||||
execvp(state->argv[0], state->argv);
|
||||
_exit(errno);
|
||||
} else if (state->pid == -1) {
|
||||
log_error("Can't fork command %s.", state->cmd_str);
|
||||
state->fails = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
|
||||
{
|
||||
#if VDO_DEBUG
|
||||
log_debug("dmeventd executes: %s.", state->cmd_str);
|
||||
#endif
|
||||
if (state->argv[0])
|
||||
return _run_command(state);
|
||||
|
||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) {
|
||||
log_error("Failed command for %s.", dm_task_get_name(dmt));
|
||||
state->fails = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->fails = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check if executed command has finished
|
||||
* Only 1 command may run */
|
||||
static int _wait_for_pid(struct dso_state *state)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (state->pid == -1)
|
||||
return 1;
|
||||
|
||||
if (!waitpid(state->pid, &status, WNOHANG))
|
||||
return 0;
|
||||
|
||||
/* Wait for finish */
|
||||
if (WIFEXITED(status)) {
|
||||
log_verbose("Child %d exited with status %d.",
|
||||
state->pid, WEXITSTATUS(status));
|
||||
state->fails = WEXITSTATUS(status) ? 1 : 0;
|
||||
} else {
|
||||
if (WIFSIGNALED(status))
|
||||
log_verbose("Child %d was terminated with status %d.",
|
||||
state->pid, WTERMSIG(status));
|
||||
state->fails = 1;
|
||||
}
|
||||
|
||||
state->pid = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt,
|
||||
enum dm_event_mask evmask __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
const char *device = dm_task_get_name(dmt);
|
||||
struct dso_state *state = *user;
|
||||
void *next = NULL;
|
||||
uint64_t start, length;
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
int needs_policy = 0;
|
||||
struct dm_task *new_dmt = NULL;
|
||||
struct dm_vdo_status_parse_result vdop = { .status = NULL };
|
||||
|
||||
#if VDO_DEBUG
|
||||
log_debug("Watch for VDO %s:%.2f%%.", state->name,
|
||||
dm_percent_to_round_float(state->percent_check, 2));
|
||||
#endif
|
||||
if (!_wait_for_pid(state)) {
|
||||
log_warn("WARNING: Skipping event, child %d is still running (%s).",
|
||||
state->pid, state->cmd_str);
|
||||
return;
|
||||
}
|
||||
|
||||
if (evmask & DM_EVENT_DEVICE_ERROR) {
|
||||
#if VDO_DEBUG
|
||||
log_debug("VDO event error.");
|
||||
#endif
|
||||
/* Error -> no need to check and do instant resize */
|
||||
state->percent = 0;
|
||||
if (_use_policy(dmt, state))
|
||||
goto out;
|
||||
|
||||
stack;
|
||||
|
||||
if (!(new_dmt = dm_task_create(DM_DEVICE_STATUS)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_set_uuid(new_dmt, dm_task_get_uuid(dmt)))
|
||||
goto_out;
|
||||
|
||||
/* Non-blocking status read */
|
||||
if (!dm_task_no_flush(new_dmt))
|
||||
log_warn("WARNING: Can't set no_flush for dm status.");
|
||||
|
||||
if (!dm_task_run(new_dmt))
|
||||
goto_out;
|
||||
|
||||
dmt = new_dmt;
|
||||
}
|
||||
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
|
||||
if (!target_type || (strcmp(target_type, "vdo") != 0)) {
|
||||
log_error("Invalid target type.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!dm_vdo_status_parse(state->mem, params, &vdop)) {
|
||||
log_error("Failed to parse status.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
state->percent = dm_make_percent(vdop.status->used_blocks,
|
||||
vdop.status->total_blocks);
|
||||
|
||||
#if VDO_DEBUG
|
||||
log_debug("VDO %s status %.2f%% " FMTu64 "/" FMTu64 ".",
|
||||
state->name, dm_percent_to_round_float(state->percent, 2),
|
||||
vdop.status->used_blocks, vdop.status->total_blocks);
|
||||
#endif
|
||||
|
||||
/* VDO pool size had changed. Clear the threshold. */
|
||||
if (state->known_data_size != vdop.status->total_blocks) {
|
||||
state->percent_check = CHECK_MINIMUM;
|
||||
state->known_data_size = vdop.status->total_blocks;
|
||||
state->fails = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger action when threshold boundary is exceeded.
|
||||
* Report 80% threshold warning when it's used above 80%.
|
||||
* Only 100% is exception as it cannot be surpassed so policy
|
||||
* action is called for: >50%, >55% ... >95%, 100%
|
||||
*/
|
||||
if ((state->percent > WARNING_THRESH) &&
|
||||
(state->percent > state->percent_check))
|
||||
log_warn("WARNING: VDO %s %s is now %.2f%% full.",
|
||||
state->name, device,
|
||||
dm_percent_to_round_float(state->percent, 2));
|
||||
if (state->percent > CHECK_MINIMUM) {
|
||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
||||
if (state->percent > state->percent_check)
|
||||
needs_policy = 1;
|
||||
state->percent_check = (state->percent / CHECK_STEP + 1) * CHECK_STEP;
|
||||
if (state->percent_check == DM_PERCENT_100)
|
||||
state->percent_check--; /* Can't get bigger then 100% */
|
||||
} else
|
||||
state->percent_check = CHECK_MINIMUM;
|
||||
|
||||
/* Reduce number of _use_policy() calls by power-of-2 factor till frequency of MAX_FAILS is reached.
|
||||
* Avoids too high number of error retries, yet shows some status messages in log regularly.
|
||||
* i.e. PV could have been pvmoved and VG/LV was locked for a while...
|
||||
*/
|
||||
if (state->fails) {
|
||||
if (state->fails++ <= state->max_fails) {
|
||||
log_debug("Postponing frequently failing policy (%u <= %u).",
|
||||
state->fails - 1, state->max_fails);
|
||||
goto out;
|
||||
}
|
||||
if (state->max_fails < MAX_FAILS)
|
||||
state->max_fails <<= 1;
|
||||
state->fails = needs_policy = 1; /* Retry failing command */
|
||||
} else
|
||||
state->max_fails = 1; /* Reset on success */
|
||||
|
||||
if (needs_policy)
|
||||
_use_policy(dmt, state);
|
||||
out:
|
||||
if (vdop.status)
|
||||
dm_pool_free(state->mem, vdop.status);
|
||||
|
||||
if (new_dmt)
|
||||
dm_task_destroy(new_dmt);
|
||||
}
|
||||
|
||||
/* Handle SIGCHLD for a thread */
|
||||
static void _sig_child(int signum __attribute__((unused)))
|
||||
{
|
||||
/* empty SIG_IGN */;
|
||||
}
|
||||
|
||||
/* Setup handler for SIGCHLD when executing external command
|
||||
* to get quick 'waitpid()' reaction
|
||||
* It will interrupt syscall just like SIGALRM and
|
||||
* invoke process_event().
|
||||
*/
|
||||
static void _init_thread_signals(struct dso_state *state)
|
||||
{
|
||||
struct sigaction act = { .sa_handler = _sig_child };
|
||||
sigset_t my_sigset;
|
||||
|
||||
sigemptyset(&my_sigset);
|
||||
|
||||
if (sigaction(SIGCHLD, &act, NULL))
|
||||
log_warn("WARNING: Failed to set SIGCHLD action.");
|
||||
else if (sigaddset(&my_sigset, SIGCHLD))
|
||||
log_warn("WARNING: Failed to add SIGCHLD to set.");
|
||||
else if (pthread_sigmask(SIG_UNBLOCK, &my_sigset, &state->old_sigset))
|
||||
log_warn("WARNING: Failed to unblock SIGCHLD.");
|
||||
else
|
||||
state->restore_sigset = 1;
|
||||
}
|
||||
|
||||
static void _restore_thread_signals(struct dso_state *state)
|
||||
{
|
||||
if (state->restore_sigset &&
|
||||
pthread_sigmask(SIG_SETMASK, &state->old_sigset, NULL))
|
||||
log_warn("WARNING: Failed to block SIGCHLD.");
|
||||
}
|
||||
|
||||
int register_device(const char *device,
|
||||
const char *uuid,
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state;
|
||||
const char *cmd;
|
||||
char *str;
|
||||
char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */
|
||||
const char *name = "pool";
|
||||
|
||||
if (!dmeventd_lvm2_init_with_pool("vdo_pool_state", state))
|
||||
goto_bad;
|
||||
|
||||
state->cmd_str = "";
|
||||
|
||||
/* Search for command for LVM- prefixed devices only */
|
||||
cmd = (strncmp(uuid, "LVM-", 4) == 0) ? "_dmeventd_vdo_command" : "";
|
||||
|
||||
if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str), cmd, device))
|
||||
goto_bad;
|
||||
|
||||
if (strncmp(cmd_str, "lvm ", 4) == 0) {
|
||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) {
|
||||
log_error("Failed to copy lvm VDO command.");
|
||||
goto bad;
|
||||
}
|
||||
} else if (cmd_str[0] == '/') {
|
||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) {
|
||||
log_error("Failed to copy VDO command.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* Find last space before 'vg/lv' */
|
||||
if (!(str = strrchr(state->cmd_str, ' ')))
|
||||
goto inval;
|
||||
|
||||
if (!(state->argv[0] = dm_pool_strndup(state->mem, state->cmd_str,
|
||||
str - state->cmd_str))) {
|
||||
log_error("Failed to copy command.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
state->argv[1] = str + 1; /* 1 argument - vg/lv */
|
||||
_init_thread_signals(state);
|
||||
} else if (cmd[0] == 0) {
|
||||
state->name = "volume"; /* What to use with 'others?' */
|
||||
} else/* Unsupported command format */
|
||||
goto inval;
|
||||
|
||||
state->pid = -1;
|
||||
state->name = name;
|
||||
*user = state;
|
||||
|
||||
log_info("Monitoring VDO %s %s.", name, device);
|
||||
|
||||
return 1;
|
||||
inval:
|
||||
log_error("Invalid command for monitoring: %s.", cmd_str);
|
||||
bad:
|
||||
log_error("Failed to monitor VDO %s %s.", name, device);
|
||||
|
||||
if (state)
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unregister_device(const char *device,
|
||||
const char *uuid __attribute__((unused)),
|
||||
int major __attribute__((unused)),
|
||||
int minor __attribute__((unused)),
|
||||
void **user)
|
||||
{
|
||||
struct dso_state *state = *user;
|
||||
const char *name = state->name;
|
||||
int i;
|
||||
|
||||
for (i = 0; !_wait_for_pid(state) && (i < 6); ++i) {
|
||||
if (i == 0)
|
||||
/* Give it 2 seconds, then try to terminate & kill it */
|
||||
log_verbose("Child %d still not finished (%s) waiting.",
|
||||
state->pid, state->cmd_str);
|
||||
else if (i == 3) {
|
||||
log_warn("WARNING: Terminating child %d.", state->pid);
|
||||
kill(state->pid, SIGINT);
|
||||
kill(state->pid, SIGTERM);
|
||||
} else if (i == 5) {
|
||||
log_warn("WARNING: Killing child %d.", state->pid);
|
||||
kill(state->pid, SIGKILL);
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (state->pid != -1)
|
||||
log_warn("WARNING: Cannot kill child %d!", state->pid);
|
||||
|
||||
_restore_thread_signals(state);
|
||||
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
log_info("No longer monitoring VDO %s %s.", name, device);
|
||||
|
||||
return 1;
|
||||
}
|
4
daemons/lvmdbusd/.gitignore
vendored
Normal file
4
daemons/lvmdbusd/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
path.py
|
||||
lvmdbusd
|
||||
lvmdb.py
|
||||
lvm_shell_proxy.py
|
70
daemons/lvmdbusd/Makefile.in
Normal file
70
daemons/lvmdbusd/Makefile.in
Normal file
@ -0,0 +1,70 @@
|
||||
#
|
||||
# Copyright (C) 2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
lvmdbuspydir = $(python3dir)/lvmdbusd
|
||||
lvmdbusdir = $(DESTDIR)$(lvmdbuspydir)
|
||||
|
||||
LVMDBUS_SRCDIR_FILES = \
|
||||
automatedproperties.py \
|
||||
background.py \
|
||||
cfg.py \
|
||||
cmdhandler.py \
|
||||
fetch.py \
|
||||
job.py \
|
||||
loader.py \
|
||||
lv.py \
|
||||
main.py \
|
||||
manager.py \
|
||||
objectmanager.py \
|
||||
pv.py \
|
||||
request.py \
|
||||
state.py \
|
||||
udevwatch.py \
|
||||
utils.py \
|
||||
vg.py \
|
||||
__init__.py
|
||||
|
||||
LVMDBUS_BUILDDIR_FILES = \
|
||||
lvmdb.py \
|
||||
lvm_shell_proxy.py \
|
||||
path.py
|
||||
|
||||
LVMDBUSD = lvmdbusd
|
||||
|
||||
CLEAN_DIRS += __pycache__
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
.PHONY: install_lvmdbusd
|
||||
|
||||
install_lvmdbusd: $(LVMDBUSD)
|
||||
$(SHOW) " [INSTALL] $<"
|
||||
$(Q) $(INSTALL_DIR) $(sbindir)
|
||||
$(Q) $(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir)
|
||||
$(Q) $(INSTALL_DIR) $(lvmdbusdir) $(lvmdbusdir)/__pycache__
|
||||
$(Q) (cd $(srcdir); $(INSTALL_DATA) $(LVMDBUS_SRCDIR_FILES) $(lvmdbusdir))
|
||||
$(Q) $(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(lvmdbusdir)
|
||||
$(Q) PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbuspydir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES)
|
||||
$(Q) $(CHMOD) 444 $(lvmdbusdir)/__pycache__/*.py[co]
|
||||
|
||||
install_lvm2: install_lvmdbusd
|
||||
|
||||
install: install_lvm2
|
||||
|
||||
DISTCLEAN_TARGETS+= \
|
||||
$(LVMDBUS_BUILDDIR_FILES) \
|
||||
$(LVMDBUSD)
|
10
daemons/lvmdbusd/__init__.py
Normal file
10
daemons/lvmdbusd/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .main import main
|
194
daemons/lvmdbusd/automatedproperties.py
Normal file
194
daemons/lvmdbusd/automatedproperties.py
Normal file
@ -0,0 +1,194 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import dbus
|
||||
import dbus.service
|
||||
from . import cfg
|
||||
from .utils import get_properties, add_properties, get_object_property_diff, \
|
||||
log_debug
|
||||
from .state import State
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyUnresolvedReferences
|
||||
class AutomatedProperties(dbus.service.Object):
|
||||
"""
|
||||
This class implements the needed interfaces for:
|
||||
org.freedesktop.DBus.Properties
|
||||
|
||||
Other classes inherit from it to get the same behavior
|
||||
"""
|
||||
|
||||
def __init__(self, object_path, search_method=None):
|
||||
dbus.service.Object.__init__(self, cfg.bus, object_path)
|
||||
self._ap_interface = []
|
||||
self._ap_o_path = object_path
|
||||
self._ap_search_method = search_method
|
||||
self.state = None
|
||||
|
||||
def dbus_object_path(self):
|
||||
return self._ap_o_path
|
||||
|
||||
def emit_data(self):
|
||||
props = {}
|
||||
|
||||
for i in self.interface():
|
||||
props[i] = AutomatedProperties._get_all_prop(self, i)
|
||||
|
||||
return self._ap_o_path, props
|
||||
|
||||
def set_interface(self, interface):
|
||||
"""
|
||||
With inheritance, we can't easily tell what interfaces a class provides,
|
||||
so we will have each class that implements an interface tell the
|
||||
base AutomatedProperties what it is they do provide. This is kind of
|
||||
clunky, and perhaps we can figure out a better way to do this later.
|
||||
:param interface: An interface the object supports
|
||||
:return:
|
||||
"""
|
||||
if interface not in self._ap_interface:
|
||||
self._ap_interface.append(interface)
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def interface(self, all_interfaces=False):
|
||||
if all_interfaces:
|
||||
cpy = list(self._ap_interface)
|
||||
cpy.extend(
|
||||
["org.freedesktop.DBus.Introspectable",
|
||||
"org.freedesktop.DBus.Properties"])
|
||||
return cpy
|
||||
|
||||
return self._ap_interface
|
||||
|
||||
@staticmethod
|
||||
def _get_prop(obj, interface_name, property_name):
|
||||
value = getattr(obj, property_name)
|
||||
# Note: If we get an exception in this handler we won't know about it,
|
||||
# only the side effect of no returned value!
|
||||
log_debug('Get (%s), type (%s), value(%s)' %
|
||||
(property_name, str(type(value)), str(value)))
|
||||
return value
|
||||
|
||||
# Properties
|
||||
# noinspection PyUnusedLocal
|
||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
in_signature='ss', out_signature='v',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Get(self, interface_name, property_name, cb, cbe):
|
||||
# Note: If we get an exception in this handler we won't know about it,
|
||||
# only the side effect of no returned value!
|
||||
r = cfg.create_request_entry(
|
||||
-1, AutomatedProperties._get_prop,
|
||||
(self, interface_name, property_name),
|
||||
cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _get_all_prop(obj, interface_name):
|
||||
if interface_name in obj.interface(True):
|
||||
# Using introspection, lets build this dynamically
|
||||
properties = get_properties(obj)
|
||||
if interface_name in properties:
|
||||
return properties[interface_name][1]
|
||||
return {}
|
||||
raise dbus.exceptions.DBusException(
|
||||
obj._ap_interface,
|
||||
'The object %s does not implement the %s interface'
|
||||
% (obj.__class__, interface_name))
|
||||
|
||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
in_signature='s', out_signature='a{sv}',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def GetAll(self, interface_name, cb, cbe):
|
||||
r = cfg.create_request_entry(
|
||||
-1, AutomatedProperties._get_all_prop,
|
||||
(self, interface_name),
|
||||
cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
in_signature='ssv')
|
||||
def Set(self, interface_name, property_name, new_value):
|
||||
setattr(self, property_name, new_value)
|
||||
self.PropertiesChanged(interface_name,
|
||||
{property_name: new_value}, [])
|
||||
|
||||
# As dbus-python does not support introspection for properties we will
|
||||
# get the autogenerated xml and then add our wanted properties to it.
|
||||
@dbus.service.method(dbus_interface=dbus.INTROSPECTABLE_IFACE,
|
||||
out_signature='s')
|
||||
def Introspect(self):
|
||||
r = dbus.service.Object.Introspect(self, self._ap_o_path, cfg.bus)
|
||||
# Look at the properties in the class
|
||||
props = get_properties(self)
|
||||
|
||||
for int_f, v in props.items():
|
||||
r = add_properties(r, int_f, v[0])
|
||||
|
||||
return r
|
||||
|
||||
@dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
signature='sa{sv}as')
|
||||
def PropertiesChanged(self, interface_name, changed_properties,
|
||||
invalidated_properties):
|
||||
log_debug(('SIGNAL: PropertiesChanged(%s, %s, %s, %s)' %
|
||||
(str(self._ap_o_path), str(interface_name),
|
||||
str(changed_properties), str(invalidated_properties))))
|
||||
|
||||
def refresh(self, search_key=None, object_state=None):
|
||||
"""
|
||||
Take the values (properties) of an object and update them with what
|
||||
lvm currently has. You can either fetch the new ones or supply the
|
||||
new state to be updated with
|
||||
:param search_key: The value to use to search for
|
||||
:param object_state: Use this as the new object state
|
||||
"""
|
||||
num_changed = 0
|
||||
|
||||
# If we can't do a lookup, bail now, this happens if we blindly walk
|
||||
# through all dbus objects as some don't have a search method, like
|
||||
# 'Manager' object.
|
||||
if not self._ap_search_method:
|
||||
return 0
|
||||
|
||||
# Either we have the new object state or we need to go fetch it
|
||||
if object_state:
|
||||
new_state = object_state
|
||||
else:
|
||||
if search_key:
|
||||
search = search_key
|
||||
else:
|
||||
search = self.lvm_id
|
||||
|
||||
new_state = self._ap_search_method([search])[0]
|
||||
assert isinstance(new_state, State)
|
||||
|
||||
assert new_state
|
||||
|
||||
# When we refresh an object the object identifiers might have changed
|
||||
# because LVM allows the user to change them (name & uuid), thus if
|
||||
# they have changed we need to update the object manager so that
|
||||
# look-ups will happen correctly
|
||||
old_id = self.state.identifiers()
|
||||
new_id = new_state.identifiers()
|
||||
if old_id[0] != new_id[0] or old_id[1] != new_id[1]:
|
||||
cfg.om.lookup_update(self, new_id[0], new_id[1])
|
||||
|
||||
# Grab the properties values, then replace the state of the object
|
||||
# and retrieve the new values.
|
||||
o_prop = get_properties(self)
|
||||
self.state = new_state
|
||||
n_prop = get_properties(self)
|
||||
|
||||
changed = get_object_property_diff(o_prop, n_prop)
|
||||
|
||||
if changed:
|
||||
for int_f, v in changed.items():
|
||||
self.PropertiesChanged(int_f, v, [])
|
||||
num_changed += 1
|
||||
return num_changed
|
150
daemons/lvmdbusd/background.py
Normal file
150
daemons/lvmdbusd/background.py
Normal file
@ -0,0 +1,150 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from . import cfg
|
||||
from .cmdhandler import options_to_cli_args, LvmExecutionMeta, call_lvm
|
||||
import dbus
|
||||
from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug
|
||||
from .request import RequestEntry
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
def pv_move_lv_cmd(move_options, lv_full_name,
|
||||
pv_source, pv_source_range, pv_dest_range_list):
|
||||
cmd = ['pvmove', '-i', '1']
|
||||
cmd.extend(options_to_cli_args(move_options))
|
||||
|
||||
if lv_full_name:
|
||||
cmd.extend(['-n', lv_full_name])
|
||||
|
||||
pv_range_append(cmd, pv_source, *pv_source_range)
|
||||
pv_dest_ranges(cmd, pv_dest_range_list)
|
||||
|
||||
return cmd
|
||||
|
||||
|
||||
def lv_merge_cmd(merge_options, lv_full_name):
|
||||
cmd = ['lvconvert', '--merge', '-i', '1']
|
||||
cmd.extend(options_to_cli_args(merge_options))
|
||||
cmd.append(lv_full_name)
|
||||
return cmd
|
||||
|
||||
|
||||
def _load_wrapper(ignored):
|
||||
cfg.load()
|
||||
|
||||
|
||||
def _move_callback(job_state, line_str):
|
||||
try:
|
||||
if line_str.count(':') == 2:
|
||||
(device, ignore, percentage) = line_str.split(':')
|
||||
|
||||
job_state.Percent = int(round(
|
||||
float(percentage.strip()[:-1]), 1))
|
||||
|
||||
# While the move is in progress we need to periodically update
|
||||
# the state to reflect where everything is at. we will do this
|
||||
# by scheduling the load to occur in the main work queue.
|
||||
r = RequestEntry(
|
||||
-1, _load_wrapper, ("_move_callback: load",), None, None, False)
|
||||
cfg.worker_q.put(r)
|
||||
except ValueError:
|
||||
log_error("Trying to parse percentage which failed for %s" % line_str)
|
||||
|
||||
|
||||
def _move_merge(interface_name, command, job_state):
|
||||
# We need to execute these command stand alone by forking & exec'ing
|
||||
# the command always as we will be getting periodic output from them on
|
||||
# the status of the long-running operation.
|
||||
|
||||
meta = LvmExecutionMeta(time.time(), 0, command)
|
||||
cfg.flightrecorder.add(meta)
|
||||
|
||||
ec, stdout, stderr = call_lvm(command, line_cb=_move_callback,
|
||||
cb_data=job_state)
|
||||
ended = time.time()
|
||||
meta.completed(ended, ec, stdout, stderr)
|
||||
|
||||
if ec == 0:
|
||||
job_state.Percent = 100
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name,
|
||||
'Exit code %s, stderr = %s' % (str(ec), stderr))
|
||||
|
||||
cfg.load()
|
||||
return '/'
|
||||
|
||||
|
||||
def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
||||
pv_dests_and_ranges, move_options, job_state):
|
||||
"""
|
||||
Common code for the pvmove handling.
|
||||
:param interface_name: What dbus interface we are providing for
|
||||
:param lv_name: Optional (None or name of LV to move)
|
||||
:param pv_src_obj: dbus object patch for source PV
|
||||
:param pv_source_range: (0,0 to ignore, else start, end segments)
|
||||
:param pv_dests_and_ranges: Array of PV object paths and start/end segs
|
||||
:param move_options: Hash with optional arguments
|
||||
:param job_state: Used to convey information about jobs between processes
|
||||
:return: '/' When complete, the empty object path
|
||||
"""
|
||||
pv_dests = []
|
||||
pv_src = cfg.om.get_object_by_path(pv_src_obj)
|
||||
if pv_src:
|
||||
|
||||
# Check to see if we are handling a move to a specific
|
||||
# destination(s)
|
||||
if len(pv_dests_and_ranges):
|
||||
for pr in pv_dests_and_ranges:
|
||||
pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
|
||||
if not pv_dbus_obj:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name,
|
||||
'PV Destination (%s) not found' % pr[0])
|
||||
|
||||
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
|
||||
|
||||
cmd = pv_move_lv_cmd(move_options,
|
||||
lv_name,
|
||||
pv_src.lvm_id,
|
||||
pv_source_range,
|
||||
pv_dests)
|
||||
|
||||
return _move_merge(interface_name, cmd, job_state)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name, 'pv_src_obj (%s) not found' % pv_src_obj)
|
||||
|
||||
|
||||
def merge(interface_name, lv_uuid, lv_name, merge_options, job_state):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||
if dbo:
|
||||
cmd = lv_merge_cmd(merge_options, dbo.lvm_id)
|
||||
return _move_merge(interface_name, cmd, job_state)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name,
|
||||
'LV with uuid %s and name %s not present!' % (lv_uuid, lv_name))
|
||||
|
||||
|
||||
def _run_cmd(req):
|
||||
log_debug(
|
||||
"_run_cmd: Running method: %s with args %s" %
|
||||
(str(req.method), str(req.arguments)))
|
||||
req.run_cmd()
|
||||
log_debug("_run_cmd: complete!")
|
||||
|
||||
|
||||
def cmd_runner(request):
|
||||
t = threading.Thread(target=_run_cmd, args=(request,),
|
||||
name="cmd_runner %s" % str(request.method))
|
||||
t.start()
|
124
daemons/lvmdbusd/cfg.py
Normal file
124
daemons/lvmdbusd/cfg.py
Normal file
@ -0,0 +1,124 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import multiprocessing
|
||||
import queue
|
||||
import itertools
|
||||
from lvmdbusd.utils import LvmDebugData
|
||||
|
||||
from lvmdbusd import path
|
||||
|
||||
LVM_CMD = os.getenv('LVM_BINARY', path.LVM_BINARY)
|
||||
|
||||
LOCK_FILE = os.getenv("LVM_DBUSD_LOCKFILE", "/var/lock/lvm/lvmdbusd")
|
||||
|
||||
# Save off the debug data needed for lvm team to debug issues
|
||||
# only used for 'fullreport' at this time.
|
||||
lvmdebug = LvmDebugData(os.getenv('LVM_DBUSD_COLLECT_LVM_DEBUG', False))
|
||||
|
||||
# This is the global object manager
|
||||
om = None
|
||||
|
||||
# This is the global bus connection
|
||||
bus = None
|
||||
|
||||
# Command line args
|
||||
args = None
|
||||
|
||||
# Set to true if we depend on external events for updates
|
||||
got_external_event = False
|
||||
|
||||
# Shared state variable across all processes
|
||||
run = multiprocessing.Value('i', 1)
|
||||
|
||||
# If this is set to true, the current setup support lvm shell, and we are
|
||||
# running in that mode of operation
|
||||
SHELL_IN_USE = None
|
||||
|
||||
# Lock used by pprint
|
||||
stdout_lock = multiprocessing.Lock()
|
||||
|
||||
worker_q = queue.Queue()
|
||||
|
||||
# Main event loop
|
||||
loop = None
|
||||
|
||||
G_LOOP_TMO = 0.5
|
||||
|
||||
# Used to instruct the daemon if we should ignore SIGTERM
|
||||
ignore_sigterm = False
|
||||
|
||||
BUS_NAME = os.getenv('LVM_DBUS_NAME', 'com.redhat.lvmdbus1')
|
||||
BASE_INTERFACE = 'com.redhat.lvmdbus1'
|
||||
PV_INTERFACE = BASE_INTERFACE + '.Pv'
|
||||
VG_INTERFACE = BASE_INTERFACE + '.Vg'
|
||||
VG_VDO_INTERFACE = BASE_INTERFACE + '.VgVdo'
|
||||
LV_INTERFACE = BASE_INTERFACE + '.Lv'
|
||||
LV_COMMON_INTERFACE = BASE_INTERFACE + '.LvCommon'
|
||||
THIN_POOL_INTERFACE = BASE_INTERFACE + '.ThinPool'
|
||||
VDO_POOL_INTERFACE = BASE_INTERFACE + '.VdoPool'
|
||||
CACHE_POOL_INTERFACE = BASE_INTERFACE + '.CachePool'
|
||||
LV_CACHED = BASE_INTERFACE + '.CachedLv'
|
||||
SNAPSHOT_INTERFACE = BASE_INTERFACE + '.Snapshot'
|
||||
MANAGER_INTERFACE = BASE_INTERFACE + '.Manager'
|
||||
JOB_INTERFACE = BASE_INTERFACE + '.Job'
|
||||
|
||||
BASE_OBJ_PATH = '/' + BASE_INTERFACE.replace('.', '/')
|
||||
PV_OBJ_PATH = BASE_OBJ_PATH + '/Pv'
|
||||
VG_OBJ_PATH = BASE_OBJ_PATH + '/Vg'
|
||||
LV_OBJ_PATH = BASE_OBJ_PATH + '/Lv'
|
||||
THIN_POOL_PATH = BASE_OBJ_PATH + "/ThinPool"
|
||||
VDO_POOL_PATH = BASE_OBJ_PATH + "/VdoPool"
|
||||
CACHE_POOL_PATH = BASE_OBJ_PATH + "/CachePool"
|
||||
HIDDEN_LV_PATH = BASE_OBJ_PATH + "/HiddenLv"
|
||||
MANAGER_OBJ_PATH = BASE_OBJ_PATH + '/Manager'
|
||||
JOB_OBJ_PATH = BASE_OBJ_PATH + '/Job'
|
||||
|
||||
# Counters for object path generation
|
||||
pv_id = itertools.count()
|
||||
vg_id = itertools.count()
|
||||
lv_id = itertools.count()
|
||||
thin_id = itertools.count()
|
||||
vdo_id = itertools.count()
|
||||
cache_pool_id = itertools.count()
|
||||
job_id = itertools.count()
|
||||
hidden_lv = itertools.count()
|
||||
|
||||
# Used to prevent circular imports...
|
||||
load = None
|
||||
event = None
|
||||
|
||||
# Boolean to denote if lvm supports VDO integration
|
||||
vdo_support = False
|
||||
|
||||
# Global cached state
|
||||
db = None
|
||||
|
||||
# lvm flight recorder
|
||||
flightrecorder = None
|
||||
|
||||
# RequestEntry ctor
|
||||
create_request_entry = None
|
||||
|
||||
# Circular debug log
|
||||
debug = None
|
||||
|
||||
|
||||
def exit_daemon():
|
||||
"""
|
||||
Exit the daemon cleanly
|
||||
:return:
|
||||
"""
|
||||
if run and loop:
|
||||
run.value = 0
|
||||
loop.quit()
|
||||
|
||||
|
||||
systemd = False
|
817
daemons/lvmdbusd/cmdhandler.py
Normal file
817
daemons/lvmdbusd/cmdhandler.py
Normal file
@ -0,0 +1,817 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import errno
|
||||
from subprocess import Popen, PIPE
|
||||
import select
|
||||
import time
|
||||
import threading
|
||||
from itertools import chain
|
||||
import collections
|
||||
import os
|
||||
|
||||
from lvmdbusd import cfg
|
||||
from lvmdbusd.utils import pv_dest_ranges, log_debug, log_error, add_no_notify,\
|
||||
make_non_block, read_decoded, extract_stack_trace, LvmBug, add_config_option, get_error_msg
|
||||
from lvmdbusd.lvm_shell_proxy import LVMShellProxy
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
|
||||
total_time = 0.0
|
||||
total_count = 0
|
||||
|
||||
# We need to prevent different threads from using the same lvm shell
|
||||
# at the same time.
|
||||
cmd_lock = threading.RLock()
|
||||
|
||||
|
||||
class LvmExecutionMeta(object):
|
||||
|
||||
def __init__(self, start, ended, cmd, ec=-1000, stdout_txt=None, stderr_txt=None):
|
||||
self.lock = threading.RLock()
|
||||
self.start = start
|
||||
self.ended = ended
|
||||
self.cmd = cmd
|
||||
self.ec = ec
|
||||
self.stdout_txt = stdout_txt
|
||||
self.stderr_txt = stderr_txt
|
||||
|
||||
def __str__(self):
|
||||
with self.lock:
|
||||
if self.ended == 0:
|
||||
ended_txt = "still running"
|
||||
self.ended = time.time()
|
||||
else:
|
||||
ended_txt = str(time.ctime(self.ended))
|
||||
|
||||
return 'EC= %d for "%s"\n' \
|
||||
"STARTED: %s, ENDED: %s, DURATION: %f\n" \
|
||||
"STDOUT=%s\n" \
|
||||
"STDERR=%s\n" % \
|
||||
(self.ec, " ".join(self.cmd), time.ctime(self.start), ended_txt, float(self.ended) - self.start,
|
||||
self.stdout_txt,
|
||||
self.stderr_txt)
|
||||
|
||||
def completed(self, end_time, ec, stdout_txt, stderr_txt):
|
||||
with self.lock:
|
||||
self.ended = end_time
|
||||
self.ec = ec
|
||||
self.stdout_txt = stdout_txt
|
||||
self.stderr_txt = stderr_txt
|
||||
|
||||
|
||||
class LvmFlightRecorder(object):
|
||||
|
||||
def __init__(self, size=16):
|
||||
self.queue = collections.deque(maxlen=size)
|
||||
self.lock = threading.RLock()
|
||||
|
||||
def add(self, lvm_exec_meta):
|
||||
with self.lock:
|
||||
self.queue.append(lvm_exec_meta)
|
||||
|
||||
def dump(self):
|
||||
with self.lock:
|
||||
if len(self.queue):
|
||||
log_error("LVM dbus flight recorder START (in order of newest to oldest)")
|
||||
for c in reversed(self.queue):
|
||||
log_error(str(c))
|
||||
log_error("LVM dbus flight recorder END")
|
||||
self.queue.clear()
|
||||
|
||||
|
||||
cfg.flightrecorder = LvmFlightRecorder()
|
||||
|
||||
|
||||
def _debug_c(cmd, exit_code, out):
|
||||
log_error('CMD= %s' % ' '.join(cmd))
|
||||
log_error(("EC= %d" % exit_code))
|
||||
log_error(("STDOUT=\n %s\n" % out[0]))
|
||||
log_error(("STDERR=\n %s\n" % out[1]))
|
||||
|
||||
|
||||
def call_lvm(command, debug=False, line_cb=None,
|
||||
cb_data=None):
|
||||
"""
|
||||
Call an executable and return a tuple of exitcode, stdout, stderr
|
||||
:param command: Command to execute
|
||||
:param debug: Dump debug to stdout
|
||||
:param line_cb: Call the supplied function for each line read from
|
||||
stdin, CALL MUST EXECUTE QUICKLY and not *block*
|
||||
otherwise call_lvm function will fail to read
|
||||
stdin/stdout. Return value of call back is ignored
|
||||
:param cb_data: Supplied to call back to allow caller access to
|
||||
its own data
|
||||
|
||||
# Callback signature
|
||||
def my_callback(my_context, line_read_stdin)
|
||||
pass
|
||||
"""
|
||||
# Prepend the full lvm executable so that we can run different versions
|
||||
# in different locations on the same box
|
||||
command.insert(0, cfg.LVM_CMD)
|
||||
command = add_no_notify(command)
|
||||
|
||||
# Ensure we get an error message when we fork & exec the lvm command line
|
||||
command = add_config_option(command, "--config", 'log/command_log_selection="log_context!=''"')
|
||||
|
||||
process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True,
|
||||
env=os.environ)
|
||||
|
||||
stdout_text = ""
|
||||
stderr_text = ""
|
||||
stdout_index = 0
|
||||
make_non_block(process.stdout)
|
||||
make_non_block(process.stderr)
|
||||
|
||||
while True and cfg.run.value != 0:
|
||||
try:
|
||||
rd_fd = [process.stdout.fileno(), process.stderr.fileno()]
|
||||
ready = select.select(rd_fd, [], [], cfg.G_LOOP_TMO)
|
||||
|
||||
for r in ready[0]:
|
||||
if r == process.stdout.fileno():
|
||||
stdout_text += read_decoded(process.stdout)
|
||||
elif r == process.stderr.fileno():
|
||||
stderr_text += read_decoded(process.stderr)
|
||||
|
||||
if line_cb is not None:
|
||||
# Process the callback for each line read!
|
||||
while True:
|
||||
i = stdout_text.find("\n", stdout_index)
|
||||
if i != -1:
|
||||
try:
|
||||
line_cb(cb_data, stdout_text[stdout_index:i])
|
||||
except BaseException as be:
|
||||
st = extract_stack_trace(be)
|
||||
log_error("call_lvm: line_cb exception: \n %s" % st)
|
||||
stdout_index = i + 1
|
||||
else:
|
||||
break
|
||||
|
||||
# Check to see if process has terminated, None when running
|
||||
if process.poll() is not None:
|
||||
stdout_text += read_decoded(process.stdout)
|
||||
stderr_text += read_decoded(process.stderr)
|
||||
break
|
||||
except IOError as ioe:
|
||||
log_debug("call_lvm:" + str(ioe))
|
||||
break
|
||||
|
||||
if process.returncode is not None:
|
||||
cfg.lvmdebug.lvm_complete()
|
||||
if debug or (process.returncode != 0 and (process.returncode != 5 and "fullreport" in command)):
|
||||
_debug_c(command, process.returncode, (stdout_text, stderr_text))
|
||||
|
||||
try:
|
||||
report_json = json.loads(stdout_text)
|
||||
except json.decoder.JSONDecodeError:
|
||||
# Some lvm commands don't return json even though we are asking for it to do so.
|
||||
return process.returncode, stdout_text, stderr_text
|
||||
|
||||
error_msg = get_error_msg(report_json)
|
||||
if error_msg:
|
||||
stderr_text += error_msg
|
||||
|
||||
return process.returncode, report_json, stderr_text
|
||||
else:
|
||||
if cfg.run.value == 0:
|
||||
raise SystemExit
|
||||
# We can bail out before the lvm command finished when we get a signal
|
||||
# which is requesting we exit
|
||||
return -errno.EINTR, "", "operation interrupted"
|
||||
|
||||
|
||||
# The actual method which gets called to invoke the lvm command, can vary
|
||||
# from forking a new process to using lvm shell
|
||||
_t_call = call_lvm
|
||||
|
||||
|
||||
def _shell_cfg():
|
||||
global _t_call
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
lvm_shell = LVMShellProxy()
|
||||
_t_call = lvm_shell.call_lvm
|
||||
cfg.SHELL_IN_USE = lvm_shell
|
||||
return True
|
||||
except Exception as e:
|
||||
_t_call = call_lvm
|
||||
cfg.SHELL_IN_USE = None
|
||||
log_error("Unable to utilize lvm shell, dropping "
|
||||
"back to fork & exec\n%s" % extract_stack_trace(e))
|
||||
return False
|
||||
|
||||
|
||||
def set_execution(shell):
|
||||
global _t_call
|
||||
with cmd_lock:
|
||||
# If the user requested lvm shell, and we are currently setup that
|
||||
# way, just return
|
||||
if cfg.SHELL_IN_USE and shell:
|
||||
return True
|
||||
else:
|
||||
if not shell and cfg.SHELL_IN_USE:
|
||||
cfg.SHELL_IN_USE.exit_shell()
|
||||
cfg.SHELL_IN_USE = None
|
||||
|
||||
_t_call = call_lvm
|
||||
if shell:
|
||||
if cfg.args.use_json:
|
||||
return _shell_cfg()
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def time_wrapper(command, debug=False):
|
||||
global total_time
|
||||
global total_count
|
||||
|
||||
with cmd_lock:
|
||||
start = time.time()
|
||||
meta = LvmExecutionMeta(start, 0, command)
|
||||
# Add the partial metadata to flight recorder, so if the command hangs
|
||||
# we will see what it was.
|
||||
cfg.flightrecorder.add(meta)
|
||||
results = _t_call(command, debug)
|
||||
ended = time.time()
|
||||
total_time += (ended - start)
|
||||
total_count += 1
|
||||
meta.completed(ended, *results)
|
||||
return results
|
||||
|
||||
|
||||
call = time_wrapper
|
||||
|
||||
|
||||
# Default cmd
|
||||
# Place default arguments for every command here.
|
||||
def _dc(cmd, args):
|
||||
c = [cmd, '--nosuffix', '--unbuffered', '--units', 'b']
|
||||
c.extend(args)
|
||||
return c
|
||||
|
||||
|
||||
def options_to_cli_args(options):
|
||||
rc = []
|
||||
for k, v in list(dict(options).items()):
|
||||
if k.startswith("-"):
|
||||
rc.append(k)
|
||||
else:
|
||||
rc.append("--%s" % k)
|
||||
if v != "":
|
||||
if isinstance(v, int):
|
||||
rc.append(str(int(v)))
|
||||
else:
|
||||
rc.append(str(v))
|
||||
return rc
|
||||
|
||||
|
||||
def pv_remove(device, remove_options):
|
||||
cmd = ['pvremove']
|
||||
cmd.extend(options_to_cli_args(remove_options))
|
||||
cmd.append(device)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def _qt(tag_name):
|
||||
return '@%s' % tag_name
|
||||
|
||||
|
||||
def _tag(operation, what, add, rm, tag_options):
|
||||
cmd = [operation]
|
||||
cmd.extend(options_to_cli_args(tag_options))
|
||||
|
||||
if isinstance(what, list):
|
||||
cmd.extend(what)
|
||||
else:
|
||||
cmd.append(what)
|
||||
|
||||
if add:
|
||||
cmd.extend(list(chain.from_iterable(
|
||||
('--addtag', _qt(x)) for x in add)))
|
||||
if rm:
|
||||
cmd.extend(list(chain.from_iterable(
|
||||
('--deltag', _qt(x)) for x in rm)))
|
||||
|
||||
return call(cmd, False)
|
||||
|
||||
|
||||
def pv_tag(pv_devices, add, rm, tag_options):
|
||||
return _tag('pvchange', pv_devices, add, rm, tag_options)
|
||||
|
||||
|
||||
def vg_tag(vg_name, add, rm, tag_options):
|
||||
return _tag('vgchange', vg_name, add, rm, tag_options)
|
||||
|
||||
|
||||
def lv_tag(lv_name, add, rm, tag_options):
|
||||
return _tag('lvchange', lv_name, add, rm, tag_options)
|
||||
|
||||
|
||||
def vg_rename(vg_uuid, new_name, rename_options):
|
||||
cmd = ['vgrename']
|
||||
cmd.extend(options_to_cli_args(rename_options))
|
||||
cmd.extend([vg_uuid, new_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_remove(vg_id, remove_options):
|
||||
cmd = ['vgremove']
|
||||
cmd.extend(options_to_cli_args(remove_options))
|
||||
cmd.extend(['-f', vg_id])
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2175220 is preventing us from doing the following
|
||||
# cmd.extend(['-f', "--select", "vg_uuid=%s" % vg_id])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_lv_create(vg_name, create_options, name, size_bytes, pv_dests):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['--size', '%dB' % size_bytes])
|
||||
cmd.extend(['--name', name, vg_name, '--yes'])
|
||||
pv_dest_ranges(cmd, pv_dests)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_lv_snapshot(vg_name, snapshot_options, name, size_bytes):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(snapshot_options))
|
||||
cmd.extend(["-s"])
|
||||
|
||||
if size_bytes != 0:
|
||||
cmd.extend(['--size', '%dB' % size_bytes])
|
||||
|
||||
cmd.extend(['--name', name, vg_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
if not thin_pool:
|
||||
cmd.extend(['--size', '%dB' % size_bytes])
|
||||
else:
|
||||
cmd.extend(['--thin', '--size', '%dB' % size_bytes])
|
||||
|
||||
cmd.extend(['--yes'])
|
||||
return cmd
|
||||
|
||||
|
||||
def vg_lv_create_linear(vg_name, create_options, name, size_bytes, thin_pool):
|
||||
cmd = _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool)
|
||||
cmd.extend(['--name', name, vg_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_lv_create_striped(vg_name, create_options, name, size_bytes,
|
||||
num_stripes, stripe_size_kb, thin_pool):
|
||||
cmd = _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool)
|
||||
cmd.extend(['--stripes', str(int(num_stripes))])
|
||||
|
||||
if stripe_size_kb != 0:
|
||||
cmd.extend(['--stripesize', str(int(stripe_size_kb))])
|
||||
|
||||
cmd.extend(['--name', name, vg_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def _vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
|
||||
num_stripes, stripe_size_kb):
|
||||
cmd = ['lvcreate']
|
||||
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
cmd.extend(['--type', raid_type])
|
||||
cmd.extend(['--size', '%dB' % size_bytes])
|
||||
|
||||
if num_stripes != 0:
|
||||
cmd.extend(['--stripes', str(int(num_stripes))])
|
||||
|
||||
if stripe_size_kb != 0:
|
||||
cmd.extend(['--stripesize', str(int(stripe_size_kb))])
|
||||
|
||||
cmd.extend(['--name', name, vg_name, '--yes'])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
|
||||
num_stripes, stripe_size_kb):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
return _vg_lv_create_raid(vg_name, create_options, name, raid_type,
|
||||
size_bytes, num_stripes, stripe_size_kb)
|
||||
|
||||
|
||||
def vg_lv_create_mirror(
|
||||
vg_name, create_options, name, size_bytes, num_copies):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
cmd.extend(['--type', 'mirror'])
|
||||
cmd.extend(['--mirrors', str(int(num_copies))])
|
||||
cmd.extend(['--size', '%dB' % size_bytes])
|
||||
cmd.extend(['--name', name, vg_name, '--yes'])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_create_cache_pool(md_full_name, data_full_name, create_options):
|
||||
cmd = ['lvconvert']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['--type', 'cache-pool', '--force', '-y',
|
||||
'--poolmetadata', md_full_name, data_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_create_thin_pool(md_full_name, data_full_name, create_options):
|
||||
cmd = ['lvconvert']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['--type', 'thin-pool', '--force', '-y',
|
||||
'--poolmetadata', md_full_name, data_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_create_vdo_pool_lv_and_lv(vg_name, pool_name, lv_name, data_size,
|
||||
virtual_size, create_options):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['-y', '--type', 'vdo', '-n', lv_name,
|
||||
'-L', '%dB' % data_size, '-V', '%dB' % virtual_size,
|
||||
"%s/%s" % (vg_name, pool_name)])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_create_vdo_pool(pool_full_name, lv_name, virtual_size, create_options):
|
||||
cmd = ['lvconvert']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['--type', 'vdo-pool', '-n', lv_name, '--force', '-y',
|
||||
'-V', '%dB' % virtual_size, pool_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_remove(lv_path, remove_options):
|
||||
cmd = ['lvremove']
|
||||
cmd.extend(options_to_cli_args(remove_options))
|
||||
cmd.extend(['-f', lv_path])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_rename(lv_path, new_name, rename_options):
|
||||
cmd = ['lvrename']
|
||||
cmd.extend(options_to_cli_args(rename_options))
|
||||
cmd.extend([lv_path, new_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_resize(lv_full_name, size_change, pv_dests,
|
||||
resize_options):
|
||||
cmd = ['lvresize', '--force']
|
||||
|
||||
cmd.extend(options_to_cli_args(resize_options))
|
||||
|
||||
if size_change < 0:
|
||||
cmd.append("-L-%dB" % (-size_change))
|
||||
else:
|
||||
cmd.append("-L+%dB" % (size_change))
|
||||
|
||||
cmd.append(lv_full_name)
|
||||
pv_dest_ranges(cmd, pv_dests)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_lv_create(lv_full_name, create_options, name, size_bytes):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['--virtualsize', '%dB' % size_bytes, '-T'])
|
||||
cmd.extend(['--name', name, lv_full_name, '--yes'])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_cache_lv(cache_pool_full_name, lv_full_name, cache_options):
|
||||
# lvconvert --type cache --cachepool VG/CachePoolLV VG/OriginLV
|
||||
cmd = ['lvconvert']
|
||||
cmd.extend(options_to_cli_args(cache_options))
|
||||
cmd.extend(['-y', '--type', 'cache', '--cachepool',
|
||||
cache_pool_full_name, lv_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_writecache_lv(cache_lv_full_name, lv_full_name, cache_options):
|
||||
# lvconvert --type writecache --cachevol VG/CacheLV VG/OriginLV
|
||||
cmd = ['lvconvert']
|
||||
cmd.extend(options_to_cli_args(cache_options))
|
||||
cmd.extend(['-y', '--type', 'writecache', '--cachevol',
|
||||
cache_lv_full_name, lv_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_detach_cache(lv_full_name, detach_options, destroy_cache):
|
||||
cmd = ['lvconvert']
|
||||
if destroy_cache:
|
||||
option = '--uncache'
|
||||
else:
|
||||
# Currently fairly dangerous
|
||||
# see: https://bugzilla.redhat.com/show_bug.cgi?id=1248972
|
||||
option = '--splitcache'
|
||||
cmd.extend(options_to_cli_args(detach_options))
|
||||
# needed to prevent interactive questions
|
||||
cmd.extend(["--yes", "--force"])
|
||||
cmd.extend([option, lv_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_vdo_compression(lv_path, enable, comp_options):
|
||||
cmd = ['lvchange', '--compression']
|
||||
if enable:
|
||||
cmd.append('y')
|
||||
else:
|
||||
cmd.append('n')
|
||||
cmd.extend(options_to_cli_args(comp_options))
|
||||
cmd.append(lv_path)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_vdo_deduplication(lv_path, enable, dedup_options):
|
||||
cmd = ['lvchange', '--deduplication']
|
||||
if enable:
|
||||
cmd.append('y')
|
||||
else:
|
||||
cmd.append('n')
|
||||
cmd.extend(options_to_cli_args(dedup_options))
|
||||
cmd.append(lv_path)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_raid_repair(lv_path, new_pvs, repair_options):
|
||||
cmd = ['lvconvert', '-y', '--repair']
|
||||
cmd.append(lv_path)
|
||||
cmd.extend(new_pvs)
|
||||
cmd.extend(options_to_cli_args(repair_options))
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def supports_json():
|
||||
cmd = ['help']
|
||||
rc, out, err = call(cmd)
|
||||
if rc == 0:
|
||||
if cfg.SHELL_IN_USE:
|
||||
return True
|
||||
else:
|
||||
if 'fullreport' in err:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def supports_vdo():
|
||||
cmd = ['segtypes']
|
||||
rc, out, err = call(cmd)
|
||||
if rc == 0:
|
||||
if "vdo" in out:
|
||||
log_debug("We have VDO support")
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def lvm_full_report_json():
|
||||
pv_columns = ['pv_name', 'pv_uuid', 'pv_fmt', 'pv_size', 'pv_free',
|
||||
'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free',
|
||||
'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
|
||||
'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
|
||||
'vg_uuid', 'pv_missing']
|
||||
|
||||
pv_seg_columns = ['pvseg_start', 'pvseg_size', 'segtype',
|
||||
'pv_uuid', 'lv_uuid', 'pv_name']
|
||||
|
||||
vg_columns = ['vg_name', 'vg_uuid', 'vg_fmt', 'vg_size', 'vg_free',
|
||||
'vg_sysid', 'vg_extent_size', 'vg_extent_count',
|
||||
'vg_free_count', 'vg_profile', 'max_lv', 'max_pv',
|
||||
'pv_count', 'lv_count', 'snap_count', 'vg_seqno',
|
||||
'vg_mda_count', 'vg_mda_free', 'vg_mda_size',
|
||||
'vg_mda_used_count', 'vg_attr', 'vg_tags']
|
||||
|
||||
lv_columns = ['lv_uuid', 'lv_name', 'lv_path', 'lv_size',
|
||||
'vg_name', 'pool_lv_uuid', 'pool_lv', 'origin_uuid',
|
||||
'origin', 'data_percent',
|
||||
'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
|
||||
'metadata_lv', 'lv_parent', 'lv_role', 'lv_layout',
|
||||
'snap_percent', 'metadata_percent', 'copy_percent',
|
||||
'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
|
||||
|
||||
lv_seg_columns = ['seg_pe_ranges', 'segtype', 'lv_uuid']
|
||||
|
||||
if cfg.vdo_support:
|
||||
lv_columns.extend(
|
||||
['vdo_operating_mode', 'vdo_compression_state', 'vdo_index_state',
|
||||
'vdo_used_size', 'vdo_saving_percent']
|
||||
)
|
||||
|
||||
lv_seg_columns.extend(
|
||||
['vdo_compression', 'vdo_deduplication',
|
||||
'vdo_use_metadata_hints', 'vdo_minimum_io_size',
|
||||
'vdo_block_map_cache_size', 'vdo_block_map_era_length',
|
||||
'vdo_use_sparse_index', 'vdo_index_memory_size',
|
||||
'vdo_slab_size', 'vdo_ack_threads', 'vdo_bio_threads',
|
||||
'vdo_bio_rotation', 'vdo_cpu_threads', 'vdo_hash_zone_threads',
|
||||
'vdo_logical_threads', 'vdo_physical_threads',
|
||||
'vdo_max_discard', 'vdo_write_policy', 'vdo_header_size'])
|
||||
|
||||
cmd = _dc('fullreport', [
|
||||
'-a', # Need hidden too
|
||||
'--configreport', 'pv', '-o', ','.join(pv_columns),
|
||||
'--configreport', 'vg', '-o', ','.join(vg_columns),
|
||||
'--configreport', 'lv', '-o', ','.join(lv_columns),
|
||||
'--configreport', 'seg', '-o', ','.join(lv_seg_columns),
|
||||
'--configreport', 'pvseg', '-o', ','.join(pv_seg_columns)
|
||||
])
|
||||
|
||||
# We are running the fullreport command, we will ask lvm to output the debug
|
||||
# data, so we can have the required information for lvm to debug the fullreport failures.
|
||||
# Note: this is disabled by default and can be enabled with env. var.
|
||||
# LVM_DBUSD_COLLECT_LVM_DEBUG=True
|
||||
fn = cfg.lvmdebug.setup()
|
||||
if fn is not None:
|
||||
add_config_option(cmd, "--config", "log {level=7 file=%s syslog=0}" % fn)
|
||||
|
||||
rc, out, err = call(cmd)
|
||||
# When we have an exported vg the exit code of lvs or fullreport will be 5
|
||||
if rc == 0 or rc == 5:
|
||||
if type(out) != dict:
|
||||
raise LvmBug("lvm likely returned invalid JSON, lvm exit code = %d, output = %s, err= %s" %
|
||||
(rc, str(out), str(err)))
|
||||
return out
|
||||
raise LvmBug("'fullreport' exited with code '%d'" % rc)
|
||||
|
||||
|
||||
def pv_resize(device, size_bytes, create_options):
|
||||
cmd = ['pvresize']
|
||||
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
if size_bytes != 0:
|
||||
cmd.extend(['--yes', '--setphysicalvolumesize', '%dB' % size_bytes])
|
||||
|
||||
cmd.extend([device])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def pv_create(create_options, devices):
|
||||
cmd = ['pvcreate', '-ff']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(devices)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def pv_allocatable(device, yes, allocation_options):
|
||||
yn = 'n'
|
||||
|
||||
if yes:
|
||||
yn = 'y'
|
||||
|
||||
cmd = ['pvchange']
|
||||
cmd.extend(options_to_cli_args(allocation_options))
|
||||
cmd.extend(['-x', yn, device])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def pv_scan(activate, cache, device_paths, major_minors, scan_options):
|
||||
cmd = ['pvscan']
|
||||
cmd.extend(options_to_cli_args(scan_options))
|
||||
|
||||
if activate:
|
||||
cmd.extend(['--activate', "ay"])
|
||||
|
||||
if cache:
|
||||
cmd.append('--cache')
|
||||
|
||||
if len(device_paths) > 0:
|
||||
for d in device_paths:
|
||||
cmd.append(d)
|
||||
|
||||
if len(major_minors) > 0:
|
||||
for mm in major_minors:
|
||||
cmd.append("%s:%s" % (mm))
|
||||
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_create(create_options, pv_devices, name):
|
||||
cmd = ['vgcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.append(name)
|
||||
cmd.extend(pv_devices)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_change(change_options, name):
|
||||
cmd = ['vgchange']
|
||||
cmd.extend(options_to_cli_args(change_options))
|
||||
cmd.append(name)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_reduce(vg_name, missing, pv_devices, reduce_options):
|
||||
cmd = ['vgreduce']
|
||||
cmd.extend(options_to_cli_args(reduce_options))
|
||||
|
||||
if missing:
|
||||
cmd.append('--removemissing')
|
||||
elif len(pv_devices) == 0:
|
||||
cmd.append('--all')
|
||||
|
||||
cmd.append(vg_name)
|
||||
cmd.extend(pv_devices)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_extend(vg_name, extend_devices, extend_options):
|
||||
cmd = ['vgextend']
|
||||
cmd.extend(options_to_cli_args(extend_options))
|
||||
cmd.append(vg_name)
|
||||
cmd.extend(extend_devices)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def _vg_value_set(name, arguments, options):
|
||||
cmd = ['vgchange']
|
||||
cmd.extend(options_to_cli_args(options))
|
||||
cmd.append(name)
|
||||
cmd.extend(arguments)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_allocation_policy(vg_name, policy, policy_options):
|
||||
return _vg_value_set(vg_name, ['--alloc', policy], policy_options)
|
||||
|
||||
|
||||
def vg_max_pv(vg_name, number, max_options):
|
||||
return _vg_value_set(vg_name, ['--maxphysicalvolumes', str(int(number))],
|
||||
max_options)
|
||||
|
||||
|
||||
def vg_max_lv(vg_name, number, max_options):
|
||||
return _vg_value_set(vg_name, ['-l', str(int(number))], max_options)
|
||||
|
||||
|
||||
def vg_uuid_gen(vg_name, ignore, options):
|
||||
assert ignore is None
|
||||
return _vg_value_set(vg_name, ['--uuid'], options)
|
||||
|
||||
|
||||
def activate_deactivate(op, name, activate, control_flags, options):
|
||||
cmd = [op]
|
||||
cmd.extend(options_to_cli_args(options))
|
||||
|
||||
op = '-a'
|
||||
|
||||
if control_flags:
|
||||
# Autoactivation
|
||||
if (1 << 0) & control_flags:
|
||||
op += 'a'
|
||||
# Exclusive locking (Cluster)
|
||||
if (1 << 1) & control_flags:
|
||||
op += 'e'
|
||||
|
||||
# Local node activation
|
||||
if (1 << 2) & control_flags:
|
||||
op += 'l'
|
||||
|
||||
# Activation modes
|
||||
if (1 << 3) & control_flags:
|
||||
cmd.extend(['--activationmode', 'complete'])
|
||||
elif (1 << 4) & control_flags:
|
||||
cmd.extend(['--activationmode', 'partial'])
|
||||
|
||||
# Ignore activation skip
|
||||
if (1 << 5) & control_flags:
|
||||
cmd.append('--ignoreactivationskip')
|
||||
|
||||
# Shared locking (Cluster)
|
||||
if (1 << 6) & control_flags:
|
||||
op += 's'
|
||||
|
||||
if activate:
|
||||
op += 'y'
|
||||
else:
|
||||
op += 'n'
|
||||
|
||||
cmd.append(op)
|
||||
cmd.append("-y")
|
||||
cmd.append(name)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Leave this for future debug as needed
|
||||
pass
|
257
daemons/lvmdbusd/fetch.py
Normal file
257
daemons/lvmdbusd/fetch.py
Normal file
@ -0,0 +1,257 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import errno
|
||||
|
||||
from .pv import load_pvs
|
||||
from .vg import load_vgs
|
||||
from .lv import load_lvs
|
||||
from . import cfg
|
||||
from .utils import MThreadRunner, log_debug, log_error, LvmBug, extract_stack_trace
|
||||
import threading
|
||||
import queue
|
||||
import time
|
||||
|
||||
|
||||
def _main_thread_load(refresh=True, emit_signal=True):
|
||||
num_total_changes = 0
|
||||
to_remove = []
|
||||
|
||||
(changes, remove) = load_pvs(
|
||||
refresh=refresh,
|
||||
emit_signal=emit_signal,
|
||||
cache_refresh=False)[1:]
|
||||
num_total_changes += changes
|
||||
to_remove.extend(remove)
|
||||
|
||||
(changes, remove) = load_vgs(
|
||||
refresh=refresh,
|
||||
emit_signal=emit_signal,
|
||||
cache_refresh=False)[1:]
|
||||
|
||||
num_total_changes += changes
|
||||
to_remove.extend(remove)
|
||||
|
||||
(lv_changes, remove) = load_lvs(
|
||||
refresh=refresh,
|
||||
emit_signal=emit_signal,
|
||||
cache_refresh=False)[1:]
|
||||
|
||||
num_total_changes += lv_changes
|
||||
to_remove.extend(remove)
|
||||
|
||||
# When the LVs change it can cause another change in the VGs which is
|
||||
# missed if we don't scan through the VGs again. We could achieve this
|
||||
# the other way and re-scan the LVs, but in general there are more LVs than
|
||||
# VGs, thus this should be more efficient. This happens when a LV interface
|
||||
# changes causing the dbus object representing it to be removed and
|
||||
# recreated.
|
||||
if refresh and lv_changes > 0:
|
||||
(changes, remove) = load_vgs(
|
||||
refresh=refresh,
|
||||
emit_signal=emit_signal,
|
||||
cache_refresh=False)[1:]
|
||||
|
||||
num_total_changes += changes
|
||||
to_remove.extend(remove)
|
||||
|
||||
# Remove any objects that are no longer needed. We do this after we process
|
||||
# all the objects to ensure that references still exist for objects that
|
||||
# are processed after them.
|
||||
to_remove.reverse()
|
||||
for i in to_remove:
|
||||
dbus_obj = cfg.om.get_object_by_path(i)
|
||||
if dbus_obj:
|
||||
cfg.om.remove_object(dbus_obj, True)
|
||||
num_total_changes += 1
|
||||
|
||||
return num_total_changes
|
||||
|
||||
|
||||
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True,
|
||||
need_main_thread=True):
|
||||
# Go through and load all the PVs, VGs and LVs
|
||||
if cache_refresh:
|
||||
cfg.db.refresh(log)
|
||||
|
||||
if need_main_thread:
|
||||
rc = MThreadRunner(_main_thread_load, refresh, emit_signal).done()
|
||||
else:
|
||||
rc = _main_thread_load(refresh, emit_signal)
|
||||
|
||||
return rc
|
||||
|
||||
|
||||
# Even though lvm can handle multiple changes concurrently it really doesn't
|
||||
# make sense to make a 1-1 fetch of data for each change of lvm because when
|
||||
# we fetch the data once all previous changes are reflected.
|
||||
class StateUpdate(object):
|
||||
|
||||
class UpdateRequest(object):
|
||||
|
||||
def __init__(self, refresh, emit_signal, cache_refresh, log,
|
||||
need_main_thread):
|
||||
self.is_done = False
|
||||
self.refresh = refresh
|
||||
self.emit_signal = emit_signal
|
||||
self.cache_refresh = cache_refresh
|
||||
self.log = log
|
||||
self.need_main_thread = need_main_thread
|
||||
self.result = None
|
||||
self.cond = threading.Condition(threading.Lock())
|
||||
|
||||
def done(self):
|
||||
with self.cond:
|
||||
if not self.is_done:
|
||||
self.cond.wait()
|
||||
return self.result
|
||||
|
||||
def set_result(self, result):
|
||||
with self.cond:
|
||||
self.result = result
|
||||
self.is_done = True
|
||||
self.cond.notify_all()
|
||||
|
||||
@staticmethod
|
||||
def update_thread(obj):
|
||||
exception_count = 0
|
||||
queued_requests = []
|
||||
|
||||
def set_results(val):
|
||||
nonlocal queued_requests
|
||||
for idx in queued_requests:
|
||||
idx.set_result(val)
|
||||
# Only clear out the requests after we have given them a result
|
||||
# otherwise we can orphan the waiting threads, and they never
|
||||
# wake up if we get an exception
|
||||
queued_requests = []
|
||||
|
||||
def bailing(rv):
|
||||
set_results(rv)
|
||||
try:
|
||||
while True:
|
||||
item = obj.queue.get(False)
|
||||
item.set_result(rv)
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
def _load_args(requests):
|
||||
"""
|
||||
If we have multiple requests in the queue, they might not all have the same options. If any of the requests
|
||||
have an option set we need to honor it.
|
||||
"""
|
||||
refresh = any([r.refresh for r in requests])
|
||||
emit_signal = any([r.emit_signal for r in requests])
|
||||
cache_refresh = any([r.cache_refresh for r in requests])
|
||||
log = any([r.log for r in requests])
|
||||
need_main_thread = any([r.need_main_thread for r in requests])
|
||||
|
||||
return refresh, emit_signal, cache_refresh, log, need_main_thread
|
||||
|
||||
def _drain_queue(queued, incoming):
|
||||
try:
|
||||
while True:
|
||||
queued.append(incoming.get(block=False))
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
def _handle_error():
|
||||
nonlocal exception_count
|
||||
exception_count += 1
|
||||
|
||||
if exception_count >= 5:
|
||||
log_error("Too many errors in update_thread, exiting daemon")
|
||||
cfg.debug.dump()
|
||||
cfg.flightrecorder.dump()
|
||||
bailing(errno.EFAULT)
|
||||
cfg.exit_daemon()
|
||||
else:
|
||||
# Slow things down when encountering errors
|
||||
cfg.lvmdebug.complete()
|
||||
time.sleep(1)
|
||||
|
||||
while cfg.run.value != 0:
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
with obj.lock:
|
||||
wait = not obj.deferred
|
||||
obj.deferred = False
|
||||
|
||||
if len(queued_requests) == 0 and wait:
|
||||
# Note: If we don't have anything for N seconds we will
|
||||
# get a queue.Empty exception raised here
|
||||
queued_requests.append(obj.queue.get(block=True, timeout=cfg.G_LOOP_TMO))
|
||||
|
||||
# Ok we have one or the deferred queue has some,
|
||||
# check if any others and grab them too
|
||||
_drain_queue(queued_requests, obj.queue)
|
||||
|
||||
if len(queued_requests) > 1:
|
||||
log_debug("Processing %d updates!" % len(queued_requests),
|
||||
'bg_black', 'fg_light_green')
|
||||
|
||||
num_changes = load(*_load_args(queued_requests))
|
||||
# Update is done, let everyone know!
|
||||
set_results(num_changes)
|
||||
|
||||
# We retrieved OK, clear exception count
|
||||
exception_count = 0
|
||||
|
||||
except queue.Empty:
|
||||
pass
|
||||
except SystemExit:
|
||||
break
|
||||
except LvmBug as bug:
|
||||
# If a lvm bug occurred, we will dump the lvm debug data if
|
||||
# we have it.
|
||||
cfg.lvmdebug.dump()
|
||||
log_error(str(bug))
|
||||
_handle_error()
|
||||
except Exception as e:
|
||||
log_error("update_thread: \n%s" % extract_stack_trace(e))
|
||||
_handle_error()
|
||||
finally:
|
||||
cfg.lvmdebug.complete()
|
||||
|
||||
# Make sure to unblock any that may be waiting before we exit this thread
|
||||
# otherwise they hang forever ...
|
||||
bailing(Exception("update thread exiting"))
|
||||
log_debug("update thread exiting!")
|
||||
|
||||
def __init__(self):
|
||||
self.lock = threading.RLock()
|
||||
self.queue = queue.Queue()
|
||||
self.deferred = False
|
||||
|
||||
# Do initial load, with retries. During error injection testing we can and do fail here.
|
||||
count = 0
|
||||
need_refresh = False # First attempt we are building from new, any subsequent will be true
|
||||
while count < 5:
|
||||
try:
|
||||
load(refresh=need_refresh, emit_signal=False, need_main_thread=False)
|
||||
break
|
||||
except LvmBug as bug:
|
||||
count += 1
|
||||
need_refresh = True
|
||||
log_error("We encountered an lvm bug on initial load, trying again %s" % str(bug))
|
||||
|
||||
self.thread = threading.Thread(target=StateUpdate.update_thread,
|
||||
args=(self,),
|
||||
name="StateUpdate.update_thread")
|
||||
|
||||
def load(self, refresh=True, emit_signal=True, cache_refresh=True,
|
||||
log=True, need_main_thread=True):
|
||||
# Place this request on the queue and wait for it to be completed
|
||||
req = StateUpdate.UpdateRequest(refresh, emit_signal, cache_refresh,
|
||||
log, need_main_thread)
|
||||
self.queue.put(req)
|
||||
return req.done()
|
||||
|
||||
def event(self):
|
||||
with self.lock:
|
||||
self.deferred = True
|
246
daemons/lvmdbusd/job.py
Normal file
246
daemons/lvmdbusd/job.py
Normal file
@ -0,0 +1,246 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .automatedproperties import AutomatedProperties
|
||||
from .utils import job_obj_path_generate, mt_async_call
|
||||
from . import cfg
|
||||
from .cfg import JOB_INTERFACE
|
||||
import dbus
|
||||
import threading
|
||||
# noinspection PyUnresolvedReferences
|
||||
from gi.repository import GLib
|
||||
|
||||
|
||||
# Class that handles a client waiting for something to be complete. We either
|
||||
# get a timeout or the operation is done.
|
||||
class WaitingClient(object):
|
||||
|
||||
# A timeout occurred
|
||||
@staticmethod
|
||||
def _timeout(wc):
|
||||
with wc.rlock:
|
||||
if wc.in_use:
|
||||
wc.in_use = False
|
||||
# Remove ourselves from waiting client
|
||||
wc.job_state.remove_waiting_client(wc)
|
||||
wc.timer_id = -1
|
||||
mt_async_call(wc.cb, wc.job_state.Complete)
|
||||
wc.job_state = None
|
||||
|
||||
def __init__(self, job_state, tmo, cb, cbe):
|
||||
self.rlock = threading.RLock()
|
||||
self.job_state = job_state
|
||||
self.cb = cb
|
||||
self.cbe = cbe
|
||||
self.in_use = True # Indicates if object is in play
|
||||
self.timer_id = -1
|
||||
if tmo > 0:
|
||||
self.timer_id = GLib.timeout_add_seconds(
|
||||
tmo, WaitingClient._timeout, self)
|
||||
|
||||
# The job finished before the timer popped, and we are being notified that
|
||||
# it's done
|
||||
def notify(self):
|
||||
with self.rlock:
|
||||
if self.in_use:
|
||||
self.in_use = False
|
||||
# Clear timer
|
||||
if self.timer_id != -1:
|
||||
GLib.source_remove(self.timer_id)
|
||||
self.timer_id = -1
|
||||
|
||||
mt_async_call(self.cb, self.job_state.Complete)
|
||||
self.job_state = None
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class JobState(object):
|
||||
def __init__(self, request=None):
|
||||
self.rlock = threading.RLock()
|
||||
|
||||
self._percent = 0
|
||||
self._complete = False
|
||||
self._request = request
|
||||
self._ec = 0
|
||||
self._stderr = ''
|
||||
self._waiting_clients = []
|
||||
|
||||
# This is a lvm command that is just taking too long and doesn't
|
||||
# support background operation
|
||||
if self._request:
|
||||
# Faking the percentage when we don't have one
|
||||
self._percent = 1
|
||||
|
||||
@property
|
||||
def Percent(self):
|
||||
with self.rlock:
|
||||
return self._percent
|
||||
|
||||
@Percent.setter
|
||||
def Percent(self, value):
|
||||
with self.rlock:
|
||||
self._percent = value
|
||||
|
||||
@property
|
||||
def Complete(self):
|
||||
with self.rlock:
|
||||
if self._request:
|
||||
self._complete = self._request.is_done()
|
||||
|
||||
return self._complete
|
||||
|
||||
@Complete.setter
|
||||
def Complete(self, value):
|
||||
with self.rlock:
|
||||
self._complete = value
|
||||
self._percent = 100
|
||||
self.notify_waiting_clients()
|
||||
|
||||
@property
|
||||
def GetError(self):
|
||||
with self.rlock:
|
||||
if self.Complete:
|
||||
if self._request:
|
||||
(rc, error) = self._request.get_errors()
|
||||
return (rc, str(error))
|
||||
else:
|
||||
return (self._ec, self._stderr)
|
||||
else:
|
||||
return (-1, 'Job is not complete!')
|
||||
|
||||
def dtor(self):
|
||||
with self.rlock:
|
||||
self._request = None
|
||||
|
||||
@property
|
||||
def Result(self):
|
||||
with self.rlock:
|
||||
if self._request:
|
||||
return self._request.result()
|
||||
return '/'
|
||||
|
||||
def add_waiting_client(self, client):
|
||||
with self.rlock:
|
||||
# Avoid race condition where it goes complete before we get added
|
||||
# to the list of waiting clients
|
||||
if self.Complete:
|
||||
client.notify()
|
||||
else:
|
||||
self._waiting_clients.append(client)
|
||||
|
||||
def remove_waiting_client(self, client):
|
||||
# If a waiting client timer pops before the job is done we will allow
|
||||
# the client to remove themselves from the list. As we have a lock
|
||||
# here and a lock in the waiting client too, and they can be obtained
|
||||
# in different orders, a deadlock can occur.
|
||||
# As this remove is really optional, we will try to acquire the lock
|
||||
# and remove. If we are unsuccessful it's not fatal, we just delay
|
||||
# the time when the objects can be garbage collected by python
|
||||
if self.rlock.acquire(False):
|
||||
try:
|
||||
self._waiting_clients.remove(client)
|
||||
finally:
|
||||
self.rlock.release()
|
||||
|
||||
def notify_waiting_clients(self):
|
||||
with self.rlock:
|
||||
for c in self._waiting_clients:
|
||||
c.notify()
|
||||
|
||||
self._waiting_clients = []
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class Job(AutomatedProperties):
|
||||
_Percent_meta = ('d', JOB_INTERFACE)
|
||||
_Complete_meta = ('b', JOB_INTERFACE)
|
||||
_Result_meta = ('o', JOB_INTERFACE)
|
||||
_GetError_meta = ('(is)', JOB_INTERFACE)
|
||||
|
||||
def __init__(self, request, job_state=None):
|
||||
super(Job, self).__init__(job_obj_path_generate())
|
||||
self.set_interface(JOB_INTERFACE)
|
||||
|
||||
if job_state:
|
||||
self.state = job_state
|
||||
else:
|
||||
self.state = JobState(request)
|
||||
|
||||
@property
|
||||
def Percent(self):
|
||||
return dbus.Double(float(self.state.Percent))
|
||||
|
||||
@property
|
||||
def Complete(self):
|
||||
return dbus.Boolean(self.state.Complete)
|
||||
|
||||
@staticmethod
|
||||
def _signal_complete(obj):
|
||||
obj.PropertiesChanged(
|
||||
JOB_INTERFACE, dict(Complete=dbus.Boolean(obj.state.Complete)), [])
|
||||
|
||||
@Complete.setter
|
||||
def Complete(self, value):
|
||||
self.state.Complete = value
|
||||
mt_async_call(Job._signal_complete, self)
|
||||
|
||||
@property
|
||||
def GetError(self):
|
||||
return dbus.Struct(self.state.GetError, signature="(is)")
|
||||
|
||||
@dbus.service.method(dbus_interface=JOB_INTERFACE)
|
||||
def Remove(self):
|
||||
if self.state.Complete:
|
||||
cfg.om.remove_object(self, True)
|
||||
self.state.dtor()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
JOB_INTERFACE, 'Job is not complete!')
|
||||
|
||||
@dbus.service.method(dbus_interface=JOB_INTERFACE,
|
||||
in_signature='i',
|
||||
out_signature='b',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Wait(self, timeout, cb, cbe):
|
||||
if timeout == 0 or self.state.Complete:
|
||||
cb(dbus.Boolean(self.state.Complete))
|
||||
else:
|
||||
self.state.add_waiting_client(
|
||||
WaitingClient(self.state, timeout, cb, cbe))
|
||||
|
||||
@property
|
||||
def Result(self):
|
||||
return dbus.ObjectPath(self.state.Result)
|
||||
|
||||
@property
|
||||
def lvm_id(self):
|
||||
return str(id(self))
|
||||
|
||||
@property
|
||||
def Uuid(self):
|
||||
import uuid
|
||||
return uuid.uuid1()
|
||||
|
||||
# Override the property "getters" implementation for the job interface, so a user can query a job while the queue
|
||||
# is processing items. Originally all the property get methods were this way, but we changed this in
|
||||
# e53454d6de07de56736303dd2157c3859f6fa848
|
||||
|
||||
# Properties
|
||||
# noinspection PyUnusedLocal
|
||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
in_signature='ss', out_signature='v')
|
||||
def Get(self, interface_name, property_name):
|
||||
# Note: If we get an exception in this handler we won't know about it,
|
||||
# only the side effect of no returned value!
|
||||
return AutomatedProperties._get_prop(self, interface_name, property_name)
|
||||
|
||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
in_signature='s', out_signature='a{sv}')
|
||||
def GetAll(self, interface_name):
|
||||
return AutomatedProperties._get_all_prop(self, interface_name)
|
84
daemons/lvmdbusd/loader.py
Normal file
84
daemons/lvmdbusd/loader.py
Normal file
@ -0,0 +1,84 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from . import cfg
|
||||
|
||||
|
||||
def _compare_construction(o_state, new_state):
|
||||
# We need to check to see if the objects would get constructed
|
||||
# the same
|
||||
existing_ctor, existing_path = o_state.creation_signature()
|
||||
new_ctor, new_path = new_state.creation_signature()
|
||||
|
||||
# print("%s == %s and %s == %s" % (str(existing_ctor), str(new_ctor),
|
||||
# str(existing_path), str(new_path)))
|
||||
|
||||
return ((existing_ctor == new_ctor) and (existing_path == new_path))
|
||||
|
||||
|
||||
def common(retrieve, o_type, search_keys,
|
||||
object_path, refresh, emit_signal, cache_refresh):
|
||||
num_changes = 0
|
||||
existing_paths = []
|
||||
rc = []
|
||||
|
||||
if search_keys:
|
||||
assert isinstance(search_keys, list)
|
||||
|
||||
if cache_refresh:
|
||||
cfg.db.refresh()
|
||||
|
||||
objects = retrieve(search_keys, cache_refresh=False)
|
||||
|
||||
# If we are doing a refresh we need to know what we have in memory, what's
|
||||
# in lvm and add those that are new and remove those that are gone!
|
||||
if refresh:
|
||||
existing_paths = cfg.om.object_paths_by_type(o_type)
|
||||
|
||||
for o in objects:
|
||||
# Assume we need to add this one to dbus, unless we are refreshing,
|
||||
# and it's already present
|
||||
return_object = True
|
||||
|
||||
if refresh:
|
||||
# We are refreshing all the PVs from LVM, if this one exists
|
||||
# we need to refresh our state.
|
||||
dbus_object = cfg.om.get_object_by_uuid_lvm_id(*o.identifiers())
|
||||
|
||||
if dbus_object:
|
||||
del existing_paths[dbus_object.dbus_object_path()]
|
||||
|
||||
# If the old object state and new object state wouldn't be
|
||||
# created with the same path and same object constructor we
|
||||
# need to remove the old object and construct the new one
|
||||
# instead!
|
||||
if not _compare_construction(dbus_object.state, o):
|
||||
# Remove existing and construct new one
|
||||
cfg.om.remove_object(dbus_object, emit_signal)
|
||||
dbus_object = o.create_dbus_object(None)
|
||||
cfg.om.register_object(dbus_object, emit_signal)
|
||||
num_changes += 1
|
||||
else:
|
||||
num_changes += dbus_object.refresh(object_state=o)
|
||||
return_object = False
|
||||
|
||||
if return_object:
|
||||
dbus_object = o.create_dbus_object(object_path)
|
||||
cfg.om.register_object(dbus_object, emit_signal)
|
||||
rc.append(dbus_object)
|
||||
|
||||
object_path = None
|
||||
|
||||
to_remove = []
|
||||
if refresh:
|
||||
to_remove = list(existing_paths.keys())
|
||||
|
||||
num_changes += len(rc)
|
||||
|
||||
return rc, num_changes, to_remove
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user