mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-28 15:33:16 +03:00
Compare commits
568 Commits
dev-mornfa
...
dev-bmr-dm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
140b04655b | ||
|
|
b3cd5d2945 | ||
|
|
6b81ac5807 | ||
|
|
988ca74351 | ||
|
|
86adb6ca63 | ||
|
|
b22b7d7ba9 | ||
|
|
7995eedd35 | ||
|
|
098528513f | ||
|
|
829384f46d | ||
|
|
e96041e18f | ||
|
|
0f45aa7f31 | ||
|
|
2cf3336130 | ||
|
|
a8b9e2eccd | ||
|
|
99f55abc56 | ||
|
|
c2d814e78d | ||
|
|
9e3ef2809a | ||
|
|
48ed8ac50c | ||
|
|
ece758457d | ||
|
|
79e9bde0ea | ||
|
|
08f047eb51 | ||
|
|
9edd2258ff | ||
|
|
13c7bbf8a9 | ||
|
|
533ac4d47d | ||
|
|
f0c18fceb4 | ||
|
|
22a1337a9b | ||
|
|
969ee25a74 | ||
|
|
6cde12a013 | ||
|
|
feb8e9a790 | ||
|
|
8a74d1ec79 | ||
|
|
694c88e031 | ||
|
|
036d90bba6 | ||
|
|
664a9f4830 | ||
|
|
3ea396e9d2 | ||
|
|
819dc1845e | ||
|
|
53c08f0bba | ||
|
|
597de9d586 | ||
|
|
f072a76326 | ||
|
|
856f9cced8 | ||
|
|
ec87e88c52 | ||
|
|
f9f5aac123 | ||
|
|
1134de3c89 | ||
|
|
3b74824985 | ||
|
|
6bd5bf3cb5 | ||
|
|
1aa7fa354e | ||
|
|
5383697c78 | ||
|
|
0b05048341 | ||
|
|
41001dbfdd | ||
|
|
d62a8d2f15 | ||
|
|
f06d866110 | ||
|
|
3638c05ec9 | ||
|
|
54815fff06 | ||
|
|
666c77c0f2 | ||
|
|
cafe145ba2 | ||
|
|
974e7b9220 | ||
|
|
b59cdf0892 | ||
|
|
993fe07a3c | ||
|
|
2bbf2fa8eb | ||
|
|
1c013e7ae2 | ||
|
|
fd1782b5fc | ||
|
|
559ca8bc65 | ||
|
|
2f334afb98 | ||
|
|
88551add97 | ||
|
|
23e8e849e4 | ||
|
|
362a1a5a82 | ||
|
|
ce1528359a | ||
|
|
f02cdcff00 | ||
|
|
1f3d04cddf | ||
|
|
c78033233a | ||
|
|
83541123c8 | ||
|
|
46e6b2b86e | ||
|
|
6ac5689ce4 | ||
|
|
d11f8d4228 | ||
|
|
71dbe47619 | ||
|
|
a5b476a7d3 | ||
|
|
b3997469b5 | ||
|
|
519c309952 | ||
|
|
a161e29c59 | ||
|
|
72f754e2bc | ||
|
|
439a579aa2 | ||
|
|
649c9d4719 | ||
|
|
51f89f2fbd | ||
|
|
e06d188f0d | ||
|
|
fef3cb3f21 | ||
|
|
027fe112ec | ||
|
|
f54198eed6 | ||
|
|
0dae377fbf | ||
|
|
8bc90a25c2 | ||
|
|
b40ccdd57c | ||
|
|
78135c24b4 | ||
|
|
d9c67a9b21 | ||
|
|
e6834b3237 | ||
|
|
c0629c13fe | ||
|
|
f6473baffc | ||
|
|
3e343ba5ef | ||
|
|
6b0c464a34 | ||
|
|
9aabf441bd | ||
|
|
772b54a08b | ||
|
|
e593213b87 | ||
|
|
3cd644aeb5 | ||
|
|
a28fb37b9e | ||
|
|
a5491d3698 | ||
|
|
ca0d9a70d1 | ||
|
|
cf700151eb | ||
|
|
af1c7bf0c7 | ||
|
|
fa11ddd7df | ||
|
|
3e333e9b5c | ||
|
|
1568ed4d20 | ||
|
|
3934ade5a2 | ||
|
|
f4fa3e1a6b | ||
|
|
ce6a0f4469 | ||
|
|
33eb7d7dfb | ||
|
|
705caa8c32 | ||
|
|
c1f5ac3eca | ||
|
|
bcb875dcb1 | ||
|
|
be66243933 | ||
|
|
57534733b7 | ||
|
|
1612c570b6 | ||
|
|
b92e502695 | ||
|
|
27e6aee390 | ||
|
|
8bfcefe11a | ||
|
|
00d24511bc | ||
|
|
03762f42c1 | ||
|
|
ae88bf03a1 | ||
|
|
c3fddb0fbb | ||
|
|
697fb353dc | ||
|
|
2a7c2539c6 | ||
|
|
500fd8b9bf | ||
|
|
b4be988732 | ||
|
|
b785a50da4 | ||
|
|
2bc0525e93 | ||
|
|
85b42d7c95 | ||
|
|
c7fc06a262 | ||
|
|
268f53ed0d | ||
|
|
b93b85378d | ||
|
|
e15db15926 | ||
|
|
2972604f0c | ||
|
|
d947a815e8 | ||
|
|
d10fb73f63 | ||
|
|
64c4106219 | ||
|
|
c45e6e3c78 | ||
|
|
a7101e7bfb | ||
|
|
beb65056cf | ||
|
|
c2d4330f27 | ||
|
|
077645476c | ||
|
|
e9e35b011e | ||
|
|
86a4d47215 | ||
|
|
4a33d57143 | ||
|
|
34a4109946 | ||
|
|
7e728fe1a1 | ||
|
|
96a883a454 | ||
|
|
9ab6bdce01 | ||
|
|
681f779a3c | ||
|
|
ac3143c093 | ||
|
|
d41bab4028 | ||
|
|
3da88b8917 | ||
|
|
9cfa27f9c5 | ||
|
|
c39f3026a8 | ||
|
|
222bb2b88d | ||
|
|
b8538f5dcd | ||
|
|
c4fdcb04be | ||
|
|
0823511262 | ||
|
|
738ae4a77f | ||
|
|
47ac6a1a2e | ||
|
|
3d2c4dc034 | ||
|
|
082fcc53cc | ||
|
|
074295245b | ||
|
|
cb14bbdbc9 | ||
|
|
841c3478fd | ||
|
|
6294509cc6 | ||
|
|
1481125042 | ||
|
|
10d27998b3 | ||
|
|
a9a7c297ae | ||
|
|
023cf21848 | ||
|
|
cb305b9fc0 | ||
|
|
a0cc570f86 | ||
|
|
903569d533 | ||
|
|
d77546773b | ||
|
|
de13abdfdf | ||
|
|
2566bdfbc3 | ||
|
|
143a9d7ee6 | ||
|
|
6e1f421a6d | ||
|
|
fd37eeddd6 | ||
|
|
6b48233f25 | ||
|
|
3ec4813ba2 | ||
|
|
e8dbaf62d3 | ||
|
|
705fee709f | ||
|
|
71f4fbfbde | ||
|
|
16e9b32c2f | ||
|
|
3b6840e099 | ||
|
|
67a61cce1b | ||
|
|
d9d47b7b88 | ||
|
|
181e701cc5 | ||
|
|
a421879bb5 | ||
|
|
3472910177 | ||
|
|
088ee7618d | ||
|
|
a405b89555 | ||
|
|
88760141da | ||
|
|
b174c27d4d | ||
|
|
bfd0689d64 | ||
|
|
0ac20a8fdb | ||
|
|
d16332be72 | ||
|
|
6a8cc1dcd4 | ||
|
|
c923dee8de | ||
|
|
633aea92fb | ||
|
|
e1733a6271 | ||
|
|
114744cee1 | ||
|
|
dfe3eb12d0 | ||
|
|
d3605b81f3 | ||
|
|
4daea88516 | ||
|
|
810ab095e6 | ||
|
|
5fb71bd530 | ||
|
|
dd385eb5ac | ||
|
|
b4e8de3a31 | ||
|
|
36ce97c625 | ||
|
|
3dbb9a57ca | ||
|
|
a900d150e4 | ||
|
|
5bef18f2eb | ||
|
|
622064f00f | ||
|
|
9cee94372a | ||
|
|
3b1422c45c | ||
|
|
335707b0e2 | ||
|
|
82ecfa6f0e | ||
|
|
e944a9c635 | ||
|
|
fe70b03de2 | ||
|
|
a32d5a4afc | ||
|
|
eaa0d927a4 | ||
|
|
454782f1a3 | ||
|
|
7f63fff9c4 | ||
|
|
21c0b1134f | ||
|
|
04ae5007e3 | ||
|
|
a69ded43b0 | ||
|
|
4c629a5257 | ||
|
|
3489e68ef7 | ||
|
|
a3af8b0626 | ||
|
|
92138badd4 | ||
|
|
f6ad48f0e5 | ||
|
|
ded279f826 | ||
|
|
89d355ea04 | ||
|
|
d7b9349ce7 | ||
|
|
d8996a17d1 | ||
|
|
77f0e7a450 | ||
|
|
621398ebb7 | ||
|
|
1587236089 | ||
|
|
125cd06698 | ||
|
|
6f793d34ca | ||
|
|
7b45a1fc60 | ||
|
|
f143ad3a93 | ||
|
|
e29d4773f4 | ||
|
|
77c2d11657 | ||
|
|
02767c5eb1 | ||
|
|
03c4fee5a7 | ||
|
|
a62cd64db6 | ||
|
|
844707067b | ||
|
|
07a34184db | ||
|
|
c794c163b5 | ||
|
|
f6de196c21 | ||
|
|
110a0745cd | ||
|
|
7760665fb8 | ||
|
|
44c7bc0262 | ||
|
|
e217873ed6 | ||
|
|
cf189a572a | ||
|
|
7559d871fb | ||
|
|
a4724350e4 | ||
|
|
a25d92c88b | ||
|
|
982cf44ff0 | ||
|
|
63c5aaaaf2 | ||
|
|
c725648f6c | ||
|
|
20e336f21c | ||
|
|
6575122c63 | ||
|
|
74bf75a2f5 | ||
|
|
1545ebf938 | ||
|
|
9465963faf | ||
|
|
c5ba60827e | ||
|
|
ba2b701f2c | ||
|
|
c23e7ff2a0 | ||
|
|
679b6b5b29 | ||
|
|
bf77f71711 | ||
|
|
4c6b3f5ec3 | ||
|
|
ae76e8f0d0 | ||
|
|
7ee3ccd826 | ||
|
|
9c86d33e68 | ||
|
|
50d70eff35 | ||
|
|
134b727b4f | ||
|
|
b45e9183bc | ||
|
|
00d028fd77 | ||
|
|
3173442984 | ||
|
|
438a65dfdb | ||
|
|
a3e0d830bd | ||
|
|
6f2a617c31 | ||
|
|
69132f55ea | ||
|
|
fd1376ebef | ||
|
|
0a203070f5 | ||
|
|
5577f2f4f0 | ||
|
|
1e6a926e85 | ||
|
|
a9bc53d5f3 | ||
|
|
da1f887060 | ||
|
|
e043e03cd8 | ||
|
|
3d9957e3dd | ||
|
|
857296c823 | ||
|
|
d5adec1056 | ||
|
|
1f318dbcee | ||
|
|
7fe5e4010c | ||
|
|
ce18fb61c0 | ||
|
|
e7eb5b0696 | ||
|
|
9a06ae7b35 | ||
|
|
ac6b355978 | ||
|
|
edbdbddfb6 | ||
|
|
3d5f7f90c8 | ||
|
|
9da07f1d3e | ||
|
|
f715fefe31 | ||
|
|
2c64762a40 | ||
|
|
9c0049b1ce | ||
|
|
632dde0cbc | ||
|
|
c78b6f18d4 | ||
|
|
4f91ad64c3 | ||
|
|
756d027da5 | ||
|
|
fd29c7f3a1 | ||
|
|
7c31293221 | ||
|
|
b89ad7e2d4 | ||
|
|
3225f8d175 | ||
|
|
778b66a719 | ||
|
|
6e4c04b1be | ||
|
|
d3abc25e76 | ||
|
|
f0a4955eb1 | ||
|
|
c254743ef3 | ||
|
|
d1531ab26d | ||
|
|
1aba262edb | ||
|
|
f0268585dd | ||
|
|
8af5f54824 | ||
|
|
c069aff21b | ||
|
|
a72a805896 | ||
|
|
eeb498627c | ||
|
|
b244fffc18 | ||
|
|
da20e0c507 | ||
|
|
f8bf641095 | ||
|
|
9d558fbcc2 | ||
|
|
ba68aed836 | ||
|
|
6d998aa13d | ||
|
|
01b06cb71b | ||
|
|
43224f22e4 | ||
|
|
e8d00e0687 | ||
|
|
55f3369692 | ||
|
|
611c8b6d29 | ||
|
|
5435346052 | ||
|
|
1562cd7320 | ||
|
|
da1527d65d | ||
|
|
925268794f | ||
|
|
f3c7bd4004 | ||
|
|
682e0c898e | ||
|
|
788e4c5423 | ||
|
|
e6b5eb88f2 | ||
|
|
cf5b4a2286 | ||
|
|
f400f9db19 | ||
|
|
7a8ce8dbf7 | ||
|
|
6fba37777c | ||
|
|
08114840ca | ||
|
|
0d300b70f9 | ||
|
|
15939e3435 | ||
|
|
131c657735 | ||
|
|
e5e0e22022 | ||
|
|
e27182249a | ||
|
|
dc49e1cde0 | ||
|
|
aa2d39c2ca | ||
|
|
0cb9df3cec | ||
|
|
caaca15854 | ||
|
|
62ac80c8fa | ||
|
|
30c3bbcd9e | ||
|
|
1bed578535 | ||
|
|
bf2b1986c2 | ||
|
|
fe00b163d6 | ||
|
|
76cc477fba | ||
|
|
2fca6cdeb3 | ||
|
|
0300730cc9 | ||
|
|
9e102ecbd9 | ||
|
|
190e91231c | ||
|
|
463c86008b | ||
|
|
850606e9fa | ||
|
|
77fa958c1e | ||
|
|
d4317c0406 | ||
|
|
d34de2d912 | ||
|
|
b91e1ea95e | ||
|
|
04c77bd886 | ||
|
|
67657f1ff9 | ||
|
|
ba120640b2 | ||
|
|
0d15217a6c | ||
|
|
9f0095fa2c | ||
|
|
bf19bbbd55 | ||
|
|
4f1c1f3d6b | ||
|
|
e93058ed81 | ||
|
|
ce3c457dcc | ||
|
|
239fb95fde | ||
|
|
6cdd153b89 | ||
|
|
333fdfd4b6 | ||
|
|
8d594c409c | ||
|
|
f653b123cf | ||
|
|
c0d30da609 | ||
|
|
b7db994aba | ||
|
|
03aec36fc0 | ||
|
|
a98ceceb1d | ||
|
|
797c18d543 | ||
|
|
3f10dfd6c7 | ||
|
|
8081ee1440 | ||
|
|
cf93fe39e3 | ||
|
|
4931d0132c | ||
|
|
64a9990977 | ||
|
|
a929606f2b | ||
|
|
679f1a9e29 | ||
|
|
4f375d03dd | ||
|
|
f3a19fbe13 | ||
|
|
64b40c5554 | ||
|
|
a42c1c5728 | ||
|
|
57a16abe2c | ||
|
|
1406aba8da | ||
|
|
b7d80806b6 | ||
|
|
6fb2552ef4 | ||
|
|
a2c9ede6b3 | ||
|
|
b184d7a2ca | ||
|
|
3fa66d1036 | ||
|
|
f207a6d353 | ||
|
|
5e38b8439a | ||
|
|
a8fc483ca4 | ||
|
|
7bd1559db6 | ||
|
|
ea846f1ca2 | ||
|
|
02e10f4ccd | ||
|
|
cc560b75aa | ||
|
|
007ac0d36f | ||
|
|
1b43d63368 | ||
|
|
d5cef7413f | ||
|
|
8da7915222 | ||
|
|
2ec51e6185 | ||
|
|
257f7febc7 | ||
|
|
24a92e08c0 | ||
|
|
c3d351ec9b | ||
|
|
e213aa17bd | ||
|
|
cdb7ce6f17 | ||
|
|
100daa7fd8 | ||
|
|
68e8030fe7 | ||
|
|
13e87045fd | ||
|
|
9c2a6de68f | ||
|
|
f5199a1cbd | ||
|
|
d758115786 | ||
|
|
5cfd7074af | ||
|
|
5e0a8fa981 | ||
|
|
2bbf0425e7 | ||
|
|
5420edd56e | ||
|
|
e28ff7e0fc | ||
|
|
d748b3455d | ||
|
|
3eb2d4d2ce | ||
|
|
20e9ec3583 | ||
|
|
62e7a6ca1a | ||
|
|
fabc19b73c | ||
|
|
b5b3ad14a8 | ||
|
|
7de6153395 | ||
|
|
ed8ea6cb2f | ||
|
|
725136b57e | ||
|
|
1806694928 | ||
|
|
e3ccf98023 | ||
|
|
abcab54cca | ||
|
|
3d845e492a | ||
|
|
e047f04394 | ||
|
|
e587b0677b | ||
|
|
be23fae488 | ||
|
|
ca67cf84df | ||
|
|
53aff9322e | ||
|
|
1d832aef09 | ||
|
|
8e509b5dd5 | ||
|
|
f25132b354 | ||
|
|
1dfedcc179 | ||
|
|
cbdf514bbc | ||
|
|
29c709f591 | ||
|
|
eadebc3b61 | ||
|
|
ed2a08bf25 | ||
|
|
5232fd13f3 | ||
|
|
b8dfd7a53d | ||
|
|
28f18404a7 | ||
|
|
3c46428fcd | ||
|
|
950a21d58a | ||
|
|
05934d2538 | ||
|
|
2cea1c1bd9 | ||
|
|
bf5cb4af8e | ||
|
|
87578b5d94 | ||
|
|
6d35c69b06 | ||
|
|
dc5190de74 | ||
|
|
e8c11c7df0 | ||
|
|
c21f1ba07a | ||
|
|
91f737383c | ||
|
|
5bbf083cd1 | ||
|
|
9fc6b654f5 | ||
|
|
7fca7f196d | ||
|
|
81c038934c | ||
|
|
76a0dffe6f | ||
|
|
bda26acf70 | ||
|
|
22ae43a11e | ||
|
|
991d646354 | ||
|
|
32527861d0 | ||
|
|
26f4b1da88 | ||
|
|
079895b8be | ||
|
|
7a5a4f952e | ||
|
|
88421c883e | ||
|
|
2d10a6f6ae | ||
|
|
7a588bce7b | ||
|
|
c90ee0414d | ||
|
|
3f05e662bb | ||
|
|
b09ac72624 | ||
|
|
75aa3e951f | ||
|
|
224e30a4b1 | ||
|
|
31f1375d23 | ||
|
|
4f6660db7d | ||
|
|
74a81a4577 | ||
|
|
bc52f07a8f | ||
|
|
636bcb020a | ||
|
|
9fb93fcd90 | ||
|
|
bee2df3903 | ||
|
|
796dc9c91c | ||
|
|
3542fce0fb | ||
|
|
abdfb1e75b | ||
|
|
6a171bbdf5 | ||
|
|
9273b1a964 | ||
|
|
9c7063ef89 | ||
|
|
79844b9066 | ||
|
|
fee09f0964 | ||
|
|
4ce5b5fdf3 | ||
|
|
a3473e60db | ||
|
|
dd4e6b4e7e | ||
|
|
16e8006eb0 | ||
|
|
c18e969e30 | ||
|
|
0eea780bce | ||
|
|
0480b4743a | ||
|
|
4daede06e5 | ||
|
|
f48a4c391c | ||
|
|
11e0dc40dc | ||
|
|
8f25606d3a | ||
|
|
fc65269d68 | ||
|
|
5a0197121b | ||
|
|
b769183a98 | ||
|
|
9c39d635b6 | ||
|
|
25e7178e59 | ||
|
|
d2c2718c11 | ||
|
|
4388ab477c | ||
|
|
3706abde5e | ||
|
|
13fea87960 | ||
|
|
d6b6246864 | ||
|
|
299a3be0d3 | ||
|
|
bf73ccb848 | ||
|
|
08a82aa940 | ||
|
|
e0a62b8fdc | ||
|
|
b120454b50 | ||
|
|
f0ff3e9982 | ||
|
|
79ec8eb93c | ||
|
|
8b6b90b073 | ||
|
|
244ca7ee77 | ||
|
|
c5b4327f3d | ||
|
|
923902013c | ||
|
|
5d8b31ffad | ||
|
|
4946c64092 | ||
|
|
3be3eb2995 | ||
|
|
0ba332e82a | ||
|
|
15a563c376 | ||
|
|
ea5c1b0a73 | ||
|
|
a1474b98f9 | ||
|
|
90cbc5576f | ||
|
|
8c9ab2a4dd | ||
|
|
3f0434057b | ||
|
|
beb229e1e8 | ||
|
|
6cc37275ce | ||
|
|
ae0014e2df | ||
|
|
afcf472464 | ||
|
|
de6deec3b8 |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,13 +1,16 @@
|
||||
*.5
|
||||
*.7
|
||||
*.8
|
||||
*.a
|
||||
*.d
|
||||
*.o
|
||||
*.orig
|
||||
*.pc
|
||||
*.pot
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*.swp
|
||||
*.sw*
|
||||
*~
|
||||
|
||||
.export.sym
|
||||
@@ -17,11 +20,11 @@
|
||||
Makefile
|
||||
make.tmpl
|
||||
|
||||
configure.h
|
||||
version.h
|
||||
|
||||
/autom4te.cache/
|
||||
/autoscan.log
|
||||
/config.log
|
||||
/config.status
|
||||
/configure.scan
|
||||
/cscope.out
|
||||
/tags
|
||||
/tmp/
|
||||
|
||||
45
Makefile.in
45
Makefile.in
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@@ -15,6 +15,8 @@
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
|
||||
SUBDIRS = conf daemons include lib libdaemon libdm man scripts tools
|
||||
|
||||
@@ -91,12 +93,41 @@ cscope.out:
|
||||
all: cscope.out
|
||||
endif
|
||||
DISTCLEAN_TARGETS += cscope.out
|
||||
CLEAN_DIRS += autom4te.cache
|
||||
|
||||
check check_system check_cluster check_local check_lvmetad unit: all
|
||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld unit: all
|
||||
$(MAKE) -C test $(@)
|
||||
|
||||
conf.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
|
||||
rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec
|
||||
|
||||
generate: conf.generate
|
||||
$(MAKE) -C conf generate
|
||||
|
||||
@@ -192,3 +223,13 @@ memcheck: test-programs
|
||||
ruby-test:
|
||||
$(RUBY) report-generators/test/ts.rb
|
||||
endif
|
||||
|
||||
ifneq ($(shell which ctags),)
|
||||
.PHONY: tags
|
||||
all: tags
|
||||
tags:
|
||||
test -z "$(shell find $(top_srcdir) -type f -name '*.[ch]' -newer tags | head -1)" || $(RM) tags
|
||||
test -f tags || find $(top_srcdir) -maxdepth 4 -type f -name '*.[ch]' -exec ctags -a '{}' +
|
||||
|
||||
DISTCLEAN_TARGETS += tags
|
||||
endif
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.02.96-git (2015-03-24)
|
||||
1.02.105-git (2015-08-10)
|
||||
|
||||
146
WHATS_NEW
146
WHATS_NEW
@@ -1,5 +1,138 @@
|
||||
Version 2.02.119 -
|
||||
==================================
|
||||
Version 2.02.128 -
|
||||
===================================
|
||||
Check for valid cache mode in validation of cache segment.
|
||||
Enhance internal API cache_set_mode() and cache_set_policy().
|
||||
Enhance toollib's get_cache_params().
|
||||
Runtime detect presence of cache smq policy.
|
||||
Add demo cache-mq and cache-smq profiles.
|
||||
Add cmd profilable allocation/cache_policy,cache_settings,cache_mode.
|
||||
Require cache_check 0.5.4 for use of --clear-needs-check-flag.
|
||||
Fix lvmetad udev rules to not override SYSTEMD_WANTS, add the service instead.
|
||||
|
||||
Version 2.02.127 - 10th August 2015
|
||||
===================================
|
||||
Do not init filters, locking, lvmetad, lvmpolld if command doesn't use it.
|
||||
Order fields in struct cmd_context more logically.
|
||||
Add lock_type to lvmcache VG summary and info structs.
|
||||
Fix regression in cache causing some PVs to bypass filters (2.02.105).
|
||||
Make configure --enable-realtime the default now.
|
||||
Update .gitignore and configure.in files to reflect usage of current tree.
|
||||
|
||||
Version 2.02.126 - 24th July 2015
|
||||
=================================
|
||||
Fix long option hyphen removal. (2.02.122)
|
||||
Fix clvmd freeze if client disappears without first releasing its locks.
|
||||
Fix lvconvert segfaults while performing snapshots merge.
|
||||
Ignore errors during detection if use_blkid_wiping=1 and --force is used.
|
||||
Recognise DM_ABORT_ON_INTERNAL_ERRORS env var override in lvm logging fn.
|
||||
Fix alloc segfault when extending LV with fewer stripes than in first seg.
|
||||
Fix handling of cache policy name.
|
||||
Set cache policy before with the first lvm2 cache pool metadata commit.
|
||||
Fix detection of thin-pool overprovisioning (2.02.124).
|
||||
Fix lvmpolld segfaults on 32 bit architectures.
|
||||
Add lvmlockd lock_args validation to vg_validate.
|
||||
Fix ignored --startstopservices option if running lvmconf with systemd.
|
||||
Hide sanlock LVs when processing LVs in VG unless named or --all used.
|
||||
|
||||
Version 2.02.125 - 7th July 2015
|
||||
================================
|
||||
Fix getline memory usage in lvmpolld.
|
||||
Add support --clear-needs-check-flag for cache_check of cache pool metadata.
|
||||
Add lvmetactl for developer use only.
|
||||
Rename global/lock_retries to lvmlockd_retries.
|
||||
Replace --enable-lvmlockd by --enable-lockd-sanlock and --enable-lockd-dlm.
|
||||
|
||||
Version 2.02.124 - 3rd July 2015
|
||||
================================
|
||||
Move sending thin pool messages from resume to suspend phase.
|
||||
Report warning when pool is overprovisioned and not auto resized.
|
||||
Recognize free-form date/time values for lv_time field in selection criteria.
|
||||
Added experimental lvmlockd with configure --enable-lvmlockd.
|
||||
Fix regression in select to match string fields if using synonyms (2.02.123).
|
||||
Fix regression when printing more lv names via display_lvname (2.02.122).
|
||||
Add missing error logging to unlock_vg and sync_local_dev_names callers.
|
||||
|
||||
Version 2.02.123 - 30th June 2015
|
||||
=================================
|
||||
Add report/time_format lvm.conf option to define time format for report.
|
||||
Fix makefile shell compare == when building lvmetad lvmpolld (2.02.120).
|
||||
Add --type full to lvmconfig for full configuration tree view.
|
||||
Add undocumented environment variables to lvm man page. (2.02.119)
|
||||
Add device synchronization point before activating a new snapshot.
|
||||
Add --withspaces to lvmconfig to add spaces in output for better readability.
|
||||
Add custom main function to libdaemon.
|
||||
Use lvmetad to track out-of-date metadata discovered.
|
||||
|
||||
Version 2.02.122 - 20th June 2015
|
||||
=================================
|
||||
Flush stdout before printing to stderr.
|
||||
Use pre-allocated buffer for printed LV names in display_lvname.
|
||||
Support thins with size of external origin unaligned with thin pool chunk.
|
||||
Allow extension of reduced thin volumes with external origins.
|
||||
Consider snapshot and origin LV as unusable if component devices suspended.
|
||||
Fix lvmconfig segfault on settings with undefined default value (2.02.120).
|
||||
Add explicit 's' (shared) LV activation mode.
|
||||
Ignore hyphens in long options names (i.e. --long-option == --longoption).
|
||||
|
||||
Version 2.02.121 - 12th June 2015
|
||||
=================================
|
||||
Distinguish between on-disk and lvmetad versions of text metadata.
|
||||
Remove DL_LIBS from Makefiles for daemons that don't need them.
|
||||
Zero errno in before strtoul call in dmsetup if tested after the call.
|
||||
Zero errno in before strtoul call in lvmpolld.
|
||||
Fix a segfault in pvscan --cache --background command.
|
||||
Fix test for AREA_PV when checking for failed mirrors.
|
||||
Do not use --sysinit in lvm2-activation{-early,-net}.service if lvmpolld used.
|
||||
Maintain outdated PV info in lvmetad till all old metadata is gone from disk.
|
||||
Do not fail polling when poll LV not found (already finished or removed).
|
||||
Replace poll_get_copy_vg/lv fns with vg_read() and find_lv() in polldaemon.
|
||||
Close all device fds only in before sleep call in polldaemon.
|
||||
Simplify Makefile targets that generate exported symbols.
|
||||
Move various -D settings from Makefiles to configure.h.
|
||||
|
||||
Version 2.02.120 - 15th May 2015
|
||||
================================
|
||||
Make various adjustments to Makefile compilation flags.
|
||||
Add lvmpolld debug message class.
|
||||
Add lvmpolld client mode for querying running server instance for status info.
|
||||
Fix some libdaemon socket creation and reuse error paths.
|
||||
Daemons (libdaemon) support exit on idle also in non-systemd environment.
|
||||
Provide make dist and make rpm targets
|
||||
Configure lvm.conf for use_lvmetad and use_lvmpolld.
|
||||
Add lvpoll for cmdline communication with lvmpolld.
|
||||
Add lvmpolld acting as a free-standing version of polldaemon.
|
||||
Avoid repeated identical lvmetad VG lookups in commands processing all VGs.
|
||||
Handle switches to alternative duplicate PVs efficiently with lvmetad.
|
||||
Properly validate PV size for pvcreate --restorefile.
|
||||
Fix check if pvcreate wiped device (2.02.117).
|
||||
Fix storing of vgid when caching metadata (2.02.118).
|
||||
Fix recursive lvm-config man page. (2.02.119)
|
||||
Refactor polldaemon interfaces to poll every operation by VG/LV couple
|
||||
Skip wait after testing in _wait_for_single_lv when polling finished
|
||||
Return 'None' in python for empty string properties instead of crashing.
|
||||
Distinguish signed numerical property type in reports for lvm2app library.
|
||||
Reread raid completion status immediately when progress appears to be zero.
|
||||
lvm2app closes locking on lvm_quit().
|
||||
Configure detects /run or /var/run.
|
||||
Add missing newline in clvmd --help output.
|
||||
|
||||
Version 2.02.119 - 2nd May 2015
|
||||
===============================
|
||||
New LVM_LOG_FILE_EPOCH, LVM_EXPECTED_EXIT_STATUS env vars. Man page to follow.
|
||||
Remove detailed content from lvm.conf man page: use lvmconfig instead.
|
||||
Generate complete config files with lvmconfig or 'make generate'.
|
||||
Also display info on deprecated config with lvmconfig --withcomments.
|
||||
Display version since which config is deprecated in lvmconfig --withversions.
|
||||
Add --showdeprecated to lvmconfig to also display deprecated settings.
|
||||
Hide deprecated settings in lvmconfig output for all types but current,diff.
|
||||
Introduce support for exit on idle feature in libdaemon
|
||||
Add --showunsupported to lvmconfig to also display unsupported settings.
|
||||
Display unsupported settings for lvmconfig --type current,diff only by default
|
||||
Honour lvmconfig --ignoreunsupported and --ignoreadvanced for all --type.
|
||||
Make python bindings usable with python3 (and compatible with 2.6 & 2.7).
|
||||
Add lvmconfig -l|--list as shortcut for lvmconfig --type list --withsummary.
|
||||
Add lvmconfig --type list to display plain list of configuration settings.
|
||||
Introduce lvmconfig as the preferred form of 'lvm dumpconfig'.
|
||||
Add lv_ancestors and lv_descendants reporting fields.
|
||||
Add --ignorelocal option to dumpconfig to ignore the local section.
|
||||
Close connection to lvmetad after fork.
|
||||
@@ -7,19 +140,26 @@ Version 2.02.119 -
|
||||
Split pvmove update metadata fn in an initial one and a subsequent one.
|
||||
Refactor shared pvmove and lvconvert code into new _poll files.
|
||||
Add --unconfigured option to dumpconfig to print strings unconfigured.
|
||||
Add --withfullcomments option to dumpconfig to print full comment.
|
||||
Add --withsummary option to dumpconfig to print first line - summary comment.
|
||||
Use number of device holders to help choose between duplicate PVs.
|
||||
Try to make lvmetad and non-lvmetad duplicate PV handling as similar as poss.
|
||||
Issue warnings about duplicate PVs discovered by lvmetad.
|
||||
Track alternative devices with matching PVIDs in lvmetad.
|
||||
Check for lvm binary in blkdeactivate and skip LVM processing if not present.
|
||||
Add --enable-halvm and --disable-halvm options to lvmconf script.
|
||||
Add --services, --mirrorservice and --startstopservices option to lvmconf.
|
||||
Use proper default value of global/use_lvmetad when processing lvmconf script.
|
||||
Respect allocation/cling_tag_list during intial contiguous allocation.
|
||||
Add A_PARTITION_BY_TAGS set when allocated areas should not share tags.
|
||||
Make changes persist with python addTag/removeTag.
|
||||
Set correct vgid when updating cache when writing PV metadata.
|
||||
More efficient clvmd singlenode locking emulation.
|
||||
Reject lvcreate -m with raid4/5/6 to avoid unexpected layout.
|
||||
Don't skip invalidation of cached orphans if vg write lck is held (2.02.118).
|
||||
Log relevant PV tags when using cling allocation.
|
||||
Add str_list_add_list() to combine two lists.
|
||||
Fix LV processing with selection to always do the selection on initial state.
|
||||
Add internal LV_REMOVED LV status flag.
|
||||
|
||||
Version 2.02.118 - 23rd March 2015
|
||||
==================================
|
||||
|
||||
77
WHATS_NEW_DM
77
WHATS_NEW_DM
@@ -1,5 +1,80 @@
|
||||
Version 1.02.96 -
|
||||
Version 1.02.105 -
|
||||
===================================
|
||||
Add more arg validation for dm_tree_node_add_cache_target().
|
||||
Add --alldevices switch to replace use of --force for stats create / delete.
|
||||
|
||||
Version 1.02.104 - 10th August 2015
|
||||
===================================
|
||||
Add dmstats.8 man page
|
||||
Add dmstats --segments switch to create one region per device segment.
|
||||
Add dmstats --regionid, --allregions to specify a single / all stats regions.
|
||||
Add dmstats --allprograms for stats commands that filter by program ID.
|
||||
Add dmstats --auxdata and --programid args to specify aux data and program ID.
|
||||
Add report stats sub-command to provide repeating stats reports.
|
||||
Add clear, delete, list, and print stats sub-commands.
|
||||
Add create stats sub-command and --start, --length, --areas and --areasize.
|
||||
Recognize 'dmstats' as an alias for 'dmsetup stats' when run with this name.
|
||||
Add a 'stats' command to dmsetup to configure, manage and report stats data.
|
||||
Add statistics fields to dmsetup -o.
|
||||
Add libdm-stats library to allow management of device-mapper statistics.
|
||||
Add --nosuffix to suppress dmsetup unit suffixes in report output.
|
||||
Add --units to control dmsetup report field output units.
|
||||
Add support to redisplay column headings for repeating column reports.
|
||||
Fix report header and row resource leaks.
|
||||
Report timestamps of ioctls with dmsetup -vvv.
|
||||
Recognize report field name variants without any underscores too.
|
||||
Add dmsetup --interval and --count to repeat reports at specified intervals.
|
||||
Add dm_timestamp functions to libdevmapper.
|
||||
Recognise vg/lv name format in dmsetup.
|
||||
Move size display code to libdevmapper as dm_size_to_string.
|
||||
|
||||
Version 1.02.103 - 24th July 2015
|
||||
=================================
|
||||
Introduce libdevmapper wrappers for all malloc-related functions.
|
||||
|
||||
Version 1.02.102 - 7th July 2015
|
||||
================================
|
||||
Include tool.h for default non-library use.
|
||||
Introduce format macros with embedded % such as FMTu64.
|
||||
|
||||
Version 1.02.101 - 3rd July 2015
|
||||
================================
|
||||
Add experimental support to passing messages in suspend tree.
|
||||
Add dm_report_value_cache_{set,get} to support caching during report/select.
|
||||
Add dm_report_reserved_handler to handle report reserved value actions.
|
||||
Support dynamic value in select: DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE.
|
||||
Support fuzzy names in select: DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES.
|
||||
Thin pool trace messages show a device name and major:minor.
|
||||
|
||||
Version 1.02.100 - 30th June 2015
|
||||
=================================
|
||||
Add since, after, until and before time operators to be used in selection.
|
||||
Add support for time in reports and selection: DM_REPORT_FIELD_TYPE_TIME.
|
||||
Support report reserved value ranges: DM_REPORT_FIELD_RESERVED_VALUE_RANGE.
|
||||
Support report reserved value names: DM_REPORT_FIELD_RESERVED_VALUE_NAMED.
|
||||
Add DM_CONFIG_VALUE_FMT_{INT_OCTAL,STRING_NO_QUOTES} config value format flag.
|
||||
Add DM_CONFIG_VALUE_FMT_COMMON_{ARRAY,EXTRA_SPACE} config value format flag.
|
||||
Add dm_config_value_{get,set}_format_flags to get and set config value format.
|
||||
|
||||
Version 1.02.99 - 20th June 2015
|
||||
================================
|
||||
New dm_tree_node_set_thin_pool_read_only(DM_1_02_99) for read-only thin pool.
|
||||
Enhance error message when thin-pool message fails.
|
||||
Fix dmeventd logging to avoid threaded use of static variable.
|
||||
Remove redundant dmeventd SIGALRM coded.
|
||||
|
||||
Version 1.02.98 - 12th June 2015
|
||||
================================
|
||||
Add dm_task_get_errno() to return any unexpected errno from a dm ioctl call.
|
||||
Use copy of errno made after each dm ioctl call in case errno changes later.
|
||||
|
||||
Version 1.02.97 - 15th May 2015
|
||||
===============================
|
||||
New dm_task_get_info(DM_1_02_97) supports internal_suspend state.
|
||||
New symbols are versioned and comes with versioned symbol name (DM_1_02_97).
|
||||
|
||||
Version 1.02.96 - 2nd May 2015
|
||||
==============================
|
||||
Fix selection to not match if using reserved value in criteria with >,<,>=,<.
|
||||
Fix selection to not match reserved values for size fields if using >,<,>=,<.
|
||||
Include uuid or device number in log message after ioctl failure.
|
||||
|
||||
4
aclocal.m4
vendored
4
aclocal.m4
vendored
@@ -1,6 +1,6 @@
|
||||
# generated automatically by aclocal 1.14.1 -*- Autoconf -*-
|
||||
# generated automatically by aclocal 1.15 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
|
||||
4
conf/.gitignore
vendored
Normal file
4
conf/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
command_profile_template.profile
|
||||
example.conf
|
||||
lvmlocal.conf
|
||||
metadata_profile_template.profile
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@@ -20,15 +20,19 @@ CONFDEST=lvm.conf
|
||||
CONFLOCAL=lvmlocal.conf
|
||||
|
||||
PROFILE_TEMPLATES=command_profile_template.profile metadata_profile_template.profile
|
||||
PROFILES=$(PROFILE_TEMPLATES) $(srcdir)/thin-generic.profile $(srcdir)/thin-performance.profile
|
||||
PROFILES=$(PROFILE_TEMPLATES) \
|
||||
$(srcdir)/cache-mq.profile \
|
||||
$(srcdir)/cache-smq.profile \
|
||||
$(srcdir)/thin-generic.profile \
|
||||
$(srcdir)/thin-performance.profile
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
.PHONY: install_conf install_localconf install_profiles
|
||||
|
||||
generate:
|
||||
(cat $(top_srcdir)/conf/example.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withfullcomments --ignorelocal) > example.conf.in
|
||||
(cat $(top_srcdir)/conf/lvmlocal.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withfullcomments local) > lvmlocal.conf.in
|
||||
(cat $(top_srcdir)/conf/example.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --ignorelocal --withspaces) > example.conf.in
|
||||
(cat $(top_srcdir)/conf/lvmlocal.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --withspaces local) > lvmlocal.conf.in
|
||||
|
||||
install_conf: $(CONFSRC)
|
||||
@if [ ! -e $(confdir)/$(CONFDEST) ]; then \
|
||||
|
||||
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 settins for "smq" policy
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,17 @@
|
||||
#
|
||||
# Refer to 'man lvm.conf' for further information including the file layout.
|
||||
#
|
||||
# Refer to 'man lvm.conf' for information about how settings configured in
|
||||
# this file are combined with built-in values and command line options to
|
||||
# arrive at the final values used by LVM.
|
||||
#
|
||||
# Refer to 'man lvmconfig' for information about displaying the built-in
|
||||
# and configured values used by LVM.
|
||||
#
|
||||
# If a default value is set in this file (not commented out), then a
|
||||
# new version of LVM using this file will continue using that value,
|
||||
# even if the new version of LVM changes the built-in default value.
|
||||
#
|
||||
# To put this file in a different directory and override @DEFAULT_SYS_DIR@ set
|
||||
# the environment variable LVM_SYSTEM_DIR before running the tools.
|
||||
#
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,8 +36,7 @@ local {
|
||||
# Example:
|
||||
# Set the system_id to the string 'host1'.
|
||||
# system_id = "host1"
|
||||
# This configuration option does not have a default value defined.
|
||||
# system_id=""
|
||||
# system_id = ""
|
||||
|
||||
# Configuration option local/extra_system_ids.
|
||||
# A list of extra VG system IDs the local host can access.
|
||||
@@ -49,5 +48,10 @@ local {
|
||||
# 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.
|
||||
# extra_system_ids=[]
|
||||
|
||||
# Configuration option local/host_id.
|
||||
# The lvmlockd sanlock host_id.
|
||||
# This must be a unique among all hosts,
|
||||
# and must be between 1 and 2000.
|
||||
# host_id = 0
|
||||
}
|
||||
|
||||
333
configure.in
333
configure.in
@@ -1,6 +1,6 @@
|
||||
###############################################################################
|
||||
## Copyright (C) 2000-2004 Sistina Software, Inc. All rights reserved.
|
||||
## Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved.
|
||||
## Copyright (C) 2004-2015 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
|
||||
@@ -26,10 +26,9 @@ AC_CONFIG_AUX_DIR(autoconf)
|
||||
dnl -- Get system type
|
||||
AC_CANONICAL_TARGET([])
|
||||
|
||||
AS_IF([test -z "$CFLAGS"], [COPTIMISE_FLAG="-O2"])
|
||||
case "$host_os" in
|
||||
linux*)
|
||||
CFLAGS="$CFLAGS"
|
||||
COPTIMISE_FLAG="-O2"
|
||||
CLDFLAGS="$CLDFLAGS -Wl,--version-script,.export.sym"
|
||||
ELDFLAGS="-Wl,--export-dynamic"
|
||||
# FIXME Generate list and use --dynamic-list=.dlopen.sym
|
||||
@@ -39,6 +38,10 @@ case "$host_os" in
|
||||
LIB_SUFFIX=so
|
||||
DEVMAPPER=yes
|
||||
LVMETAD=no
|
||||
LVMPOLLD=no
|
||||
LVMLOCKD=no
|
||||
LOCKDSANLOCK=no
|
||||
LOCKDDLM=no
|
||||
ODIRECT=yes
|
||||
DM_IOCTLS=yes
|
||||
SELINUX=yes
|
||||
@@ -48,7 +51,6 @@ case "$host_os" in
|
||||
;;
|
||||
darwin*)
|
||||
CFLAGS="$CFLAGS -no-cpp-precomp -fno-common"
|
||||
COPTIMISE_FLAG="-O2"
|
||||
CLDFLAGS="$CLDFLAGS"
|
||||
ELDFLAGS=
|
||||
CLDWHOLEARCHIVE="-all_load"
|
||||
@@ -68,8 +70,12 @@ esac
|
||||
dnl -- Checks for programs.
|
||||
AC_PROG_SED
|
||||
AC_PROG_AWK
|
||||
save_CFLAGS=$CFLAGS
|
||||
save_CXXFLAGS=$CXXFLAGS
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
CFLAGS=$save_CFLAGS
|
||||
CXXFLAGS=$save_CXXFLAGS
|
||||
|
||||
dnl probably no longer needed in 2008, but...
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
@@ -85,14 +91,19 @@ AC_PATH_TOOL(CSCOPE_CMD, cscope)
|
||||
dnl -- Check for header files.
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_MAJOR
|
||||
AC_HEADER_STDBOOL
|
||||
AC_HEADER_STDC
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_HEADER_TIME
|
||||
|
||||
AC_CHECK_HEADERS([locale.h stddef.h syslog.h sys/file.h sys/time.h assert.h \
|
||||
langinfo.h libgen.h signal.h sys/mman.h sys/resource.h sys/utsname.h \
|
||||
sys/wait.h time.h], ,
|
||||
[AC_MSG_ERROR(bailing out)])
|
||||
AC_CHECK_HEADERS([assert.h ctype.h dirent.h errno.h fcntl.h float.h \
|
||||
getopt.h inttypes.h langinfo.h libgen.h limits.h locale.h paths.h \
|
||||
signal.h stdarg.h stddef.h stdio.h stdlib.h string.h sys/file.h \
|
||||
sys/ioctl.h syslog.h sys/mman.h sys/param.h sys/resource.h sys/stat.h \
|
||||
sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \
|
||||
unistd.h], , [AC_MSG_ERROR(bailing out)])
|
||||
|
||||
AC_CHECK_HEADERS(termios.h sys/statvfs.h sys/timerfd.h)
|
||||
|
||||
case "$host_os" in
|
||||
linux*)
|
||||
@@ -101,16 +112,13 @@ case "$host_os" in
|
||||
AC_CHECK_HEADERS(machine/endian.h sys/disk.h,,AC_MSG_ERROR(bailing out)) ;;
|
||||
esac
|
||||
|
||||
AC_CHECK_HEADERS([ctype.h dirent.h errno.h fcntl.h getopt.h inttypes.h limits.h \
|
||||
stdarg.h stdio.h stdlib.h string.h sys/ioctl.h sys/param.h sys/stat.h \
|
||||
sys/types.h unistd.h], , [AC_MSG_ERROR(bailing out)])
|
||||
AC_CHECK_HEADERS(termios.h sys/statvfs.h)
|
||||
|
||||
################################################################################
|
||||
dnl -- Check for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
AC_CHECK_MEMBERS([struct stat.st_rdev])
|
||||
AC_CHECK_TYPES([ptrdiff_t])
|
||||
AC_STRUCT_TM
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_PID_T
|
||||
AC_TYPE_SIGNAL
|
||||
@@ -126,15 +134,13 @@ AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT64_T
|
||||
AC_CHECK_MEMBERS([struct stat.st_rdev])
|
||||
AC_STRUCT_TM
|
||||
|
||||
################################################################################
|
||||
dnl -- Check for functions
|
||||
AC_CHECK_FUNCS([ftruncate gethostname getpagesize \
|
||||
gettimeofday memset mkdir mkfifo rmdir munmap nl_langinfo setenv setlocale \
|
||||
strcasecmp strchr strcspn strspn strdup strncasecmp strerror strrchr \
|
||||
strstr strtol strtoul uname], , [AC_MSG_ERROR(bailing out)])
|
||||
AC_CHECK_FUNCS([ftruncate gethostname getpagesize gettimeofday localtime_r \
|
||||
memchr memset mkdir mkfifo munmap nl_langinfo realpath rmdir setenv \
|
||||
setlocale strcasecmp strchr strcspn strdup strerror strncasecmp strndup \
|
||||
strrchr strspn strstr strtol strtoul uname], , [AC_MSG_ERROR(bailing out)])
|
||||
AC_FUNC_ALLOCA
|
||||
AC_FUNC_CLOSEDIR_VOID
|
||||
AC_FUNC_CHOWN
|
||||
@@ -142,6 +148,7 @@ AC_FUNC_FORK
|
||||
AC_FUNC_LSTAT
|
||||
AC_FUNC_MALLOC
|
||||
AC_FUNC_MEMCMP
|
||||
AC_FUNC_MKTIME
|
||||
AC_FUNC_MMAP
|
||||
AC_FUNC_REALLOC
|
||||
AC_FUNC_STAT
|
||||
@@ -168,6 +175,9 @@ AC_SUBST(HAVE_FULL_RELRO)
|
||||
################################################################################
|
||||
dnl -- Prefix is /usr by default, the exec_prefix default is setup later
|
||||
AC_PREFIX_DEFAULT(/usr)
|
||||
if test "$prefix" = NONE; then
|
||||
datarootdir=${ac_default_prefix}/share
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Setup the ownership of the files
|
||||
@@ -198,6 +208,7 @@ AC_ARG_WITH(device-uid,
|
||||
[set the owner used for new device nodes [UID=0]]),
|
||||
DM_DEVICE_UID=$withval, DM_DEVICE_UID=0)
|
||||
AC_MSG_RESULT($DM_DEVICE_UID)
|
||||
AC_DEFINE_UNQUOTED([DM_DEVICE_UID], [$DM_DEVICE_UID], [Define default owner for device node])
|
||||
|
||||
################################################################################
|
||||
dnl -- Setup device group ownership
|
||||
@@ -208,6 +219,7 @@ AC_ARG_WITH(device-gid,
|
||||
[set the group used for new device nodes [GID=0]]),
|
||||
DM_DEVICE_GID=$withval, DM_DEVICE_GID=0)
|
||||
AC_MSG_RESULT($DM_DEVICE_GID)
|
||||
AC_DEFINE_UNQUOTED([DM_DEVICE_GID], [$DM_DEVICE_GID], [Define default group for device node])
|
||||
|
||||
################################################################################
|
||||
dnl -- Setup device mode
|
||||
@@ -218,6 +230,7 @@ AC_ARG_WITH(device-mode,
|
||||
[set the mode used for new device nodes [MODE=0600]]),
|
||||
DM_DEVICE_MODE=$withval, DM_DEVICE_MODE=0600)
|
||||
AC_MSG_RESULT($DM_DEVICE_MODE)
|
||||
AC_DEFINE_UNQUOTED([DM_DEVICE_MODE], [$DM_DEVICE_MODE], [Define default mode for device node])
|
||||
|
||||
AC_MSG_CHECKING(when to create device nodes)
|
||||
AC_ARG_WITH(device-nodes-on,
|
||||
@@ -257,8 +270,13 @@ AC_ARG_ENABLE(lvm1_fallback,
|
||||
AC_MSG_RESULT($LVM1_FALLBACK)
|
||||
|
||||
if test "$LVM1_FALLBACK" = yes; then
|
||||
DEFAULT_FALLBACK_TO_LVM1=1
|
||||
AC_DEFINE([LVM1_FALLBACK], 1, [Define to 1 if 'lvm' should fall back to using LVM1 binaries if device-mapper is missing from the kernel])
|
||||
else
|
||||
DEFAULT_FALLBACK_TO_LVM1=0
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_FALLBACK_TO_LVM1, [$DEFAULT_FALLBACK_TO_LVM1],
|
||||
[Fall back to LVM1 by default if device-mapper is missing from the kernel.])
|
||||
|
||||
################################################################################
|
||||
dnl -- format1 inclusion type
|
||||
@@ -546,6 +564,12 @@ case "$CACHE" in
|
||||
*) AC_MSG_ERROR([--with-cache parameter invalid]) ;;
|
||||
esac
|
||||
|
||||
dnl -- cache_check needs-check flag
|
||||
AC_ARG_ENABLE(cache_check_needs_check,
|
||||
AC_HELP_STRING([--disable-cache_check_needs_check],
|
||||
[required if cache_check version is < 0.5]),
|
||||
CACHE_CHECK_NEEDS_CHECK=$enableval, CACHE_CHECK_NEEDS_CHECK=yes)
|
||||
|
||||
# Test if necessary cache tools are available
|
||||
# if not - use plain defaults and warn user
|
||||
case "$CACHE" in
|
||||
@@ -559,6 +583,28 @@ case "$CACHE" in
|
||||
CACHE_CONFIGURE_WARN=y
|
||||
fi
|
||||
fi
|
||||
if test "$CACHE_CHECK_NEEDS_CHECK" = yes; then
|
||||
$CACHE_CHECK_CMD -V 2>/dev/null >conftest.tmp
|
||||
read -r CACHE_CHECK_VSN < conftest.tmp
|
||||
IFS=. read -r CACHE_CHECK_VSN_MAJOR CACHE_CHECK_VSN_MINOR CACHE_CHECK_VSN_PATCH < conftest.tmp
|
||||
rm -f conftest.tmp
|
||||
|
||||
# Require version >= 0.5.4 for --clear-needs-check-flag
|
||||
if test -z "$CACHE_CHECK_VSN_MAJOR" \
|
||||
|| test -z "$CACHE_CHECK_VSN_MINOR" \
|
||||
|| test -z "$CACHE_CHECK_VSN_PATCH"; then
|
||||
AC_MSG_WARN([$CACHE_CHECK_CMD: Bad version "$CACHE_CHECK_VSN" found])
|
||||
CACHE_CHECK_VERSION_WARN=y
|
||||
CACHE_CHECK_NEEDS_CHECK=no
|
||||
elif test "$CACHE_CHECK_VSN_MAJOR" -eq 0 ; then
|
||||
if test "$CACHE_CHECK_VSN_MINOR" -lt 5 \
|
||||
|| test "$CACHE_CHECK_VSN_MINOR" -eq 5 -a "$CACHE_CHECK_VSN_PATCH" -lt 4; then
|
||||
AC_MSG_WARN([$CACHE_CHECK_CMD: Old version "$CACHE_CHECK_VSN" found])
|
||||
CACHE_CHECK_VERSION_WARN=y
|
||||
CACHE_CHECK_NEEDS_CHECK=no
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# Empty means a config way to ignore cache dumping
|
||||
if test "$CACHE_DUMP_CMD" = "autodetect"; then
|
||||
AC_PATH_TOOL(CACHE_DUMP_CMD, cache_dump)
|
||||
@@ -586,6 +632,12 @@ case "$CACHE" in
|
||||
CACHE_CONFIGURE_WARN=y
|
||||
}
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether cache_check supports the needs-check flag])
|
||||
AC_MSG_RESULT([$CACHE_CHECK_NEEDS_CHECK])
|
||||
if test "$CACHE_CHECK_NEEDS_CHECK" = yes; then
|
||||
AC_DEFINE([CACHE_CHECK_NEEDS_CHECK], 1, [Define to 1 if the external 'cache_check' tool requires the --clear-needs-check-flag option])
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -614,8 +666,8 @@ AC_MSG_RESULT($READLINE)
|
||||
dnl -- Disable realtime clock support
|
||||
AC_MSG_CHECKING(whether to enable realtime support)
|
||||
AC_ARG_ENABLE(realtime,
|
||||
AC_HELP_STRING([--enable-realtime], [enable realtime clock support]),
|
||||
REALTIME=$enableval)
|
||||
AC_HELP_STRING([--disable-realtime], [disable realtime clock support]),
|
||||
REALTIME=$enableval, REALTIME=yes)
|
||||
AC_MSG_RESULT($REALTIME)
|
||||
|
||||
################################################################################
|
||||
@@ -645,28 +697,32 @@ pkg_config_init() {
|
||||
}
|
||||
|
||||
################################################################################
|
||||
AC_MSG_CHECKING(for default run directory)
|
||||
RUN_DIR="/run"
|
||||
test -d "/run" || RUN_DIR="/var/run"
|
||||
AC_MSG_RESULT($RUN_DIR)
|
||||
dnl -- Set up pidfile and run directory
|
||||
AH_TEMPLATE(DEFAULT_PID_DIR)
|
||||
AC_ARG_WITH(default-pid-dir,
|
||||
AC_HELP_STRING([--with-default-pid-dir=PID_DIR],
|
||||
[Default directory to keep PID files in. [/var/run]]),
|
||||
DEFAULT_PID_DIR="$withval", DEFAULT_PID_DIR="/var/run")
|
||||
[Default directory to keep PID files in. [autodetect]]),
|
||||
DEFAULT_PID_DIR="$withval", DEFAULT_PID_DIR=$RUN_DIR)
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_PID_DIR, ["$DEFAULT_PID_DIR"],
|
||||
[Default directory to keep PID files in.])
|
||||
|
||||
AH_TEMPLATE(DEFAULT_DM_RUN_DIR, [Name of default DM run directory.])
|
||||
AC_ARG_WITH(default-dm-run-dir,
|
||||
AC_HELP_STRING([--with-default-dm-run-dir=DM_RUN_DIR],
|
||||
[ Default DM run directory. [/var/run]]),
|
||||
DEFAULT_DM_RUN_DIR="$withval", DEFAULT_DM_RUN_DIR="/var/run")
|
||||
[ Default DM run directory. [autodetect]]),
|
||||
DEFAULT_DM_RUN_DIR="$withval", DEFAULT_DM_RUN_DIR=$RUN_DIR)
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_DM_RUN_DIR, ["$DEFAULT_DM_RUN_DIR"],
|
||||
[Default DM run directory.])
|
||||
|
||||
AH_TEMPLATE(DEFAULT_RUN_DIR, [Name of default LVM run directory.])
|
||||
AC_ARG_WITH(default-run-dir,
|
||||
AC_HELP_STRING([--with-default-run-dir=RUN_DIR],
|
||||
[Default LVM run directory. [/var/run/lvm]]),
|
||||
DEFAULT_RUN_DIR="$withval", DEFAULT_RUN_DIR="/var/run/lvm")
|
||||
[Default LVM run directory. [autodetect_run_dir/lvm]]),
|
||||
DEFAULT_RUN_DIR="$withval", DEFAULT_RUN_DIR="$RUN_DIR/lvm")
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_RUN_DIR, ["$DEFAULT_RUN_DIR"],
|
||||
[Default LVM run directory.])
|
||||
|
||||
@@ -1016,6 +1072,13 @@ if test "$TESTING" = yes; then
|
||||
PKG_CHECK_MODULES(CUNIT, cunit >= 2.0)
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Set LVM2 testsuite data
|
||||
TESTSUITE_DATA='${datarootdir}/lvm2-testsuite'
|
||||
# double eval needed ${datarootdir} -> ${prefix}/share -> real path
|
||||
AC_DEFINE_UNQUOTED(TESTSUITE_DATA, ["$(eval echo $(eval echo $TESTSUITE_DATA))"], [Path to testsuite data])
|
||||
|
||||
|
||||
################################################################################
|
||||
dnl -- Enable valgrind awareness of memory pools
|
||||
AC_MSG_CHECKING(whether to enable valgrind awareness of pools)
|
||||
@@ -1061,7 +1124,106 @@ AC_MSG_RESULT($LVMETAD)
|
||||
|
||||
BUILD_LVMETAD=$LVMETAD
|
||||
|
||||
################################################################################
|
||||
dnl -- Build lvmpolld
|
||||
AC_MSG_CHECKING(whether to build lvmpolld)
|
||||
AC_ARG_ENABLE(lvmpolld,
|
||||
AC_HELP_STRING([--enable-lvmpolld],
|
||||
[enable the LVM Polling Daemon]),
|
||||
LVMPOLLD=$enableval)
|
||||
AC_MSG_RESULT($LVMPOLLD)
|
||||
|
||||
BUILD_LVMPOLLD=$LVMPOLLD
|
||||
|
||||
################################################################################
|
||||
dnl -- Build lockdsanlock
|
||||
AC_MSG_CHECKING(whether to build lockdsanlock)
|
||||
AC_ARG_ENABLE(lockd-sanlock,
|
||||
AC_HELP_STRING([--enable-lockd-sanlock],
|
||||
[enable the LVM lock daemon using sanlock]),
|
||||
LOCKDSANLOCK=$enableval)
|
||||
AC_MSG_RESULT($LOCKDSANLOCK)
|
||||
|
||||
BUILD_LOCKDSANLOCK=$LOCKDSANLOCK
|
||||
|
||||
if test "$BUILD_LOCKDSANLOCK" = yes; then
|
||||
AC_DEFINE([LOCKDSANLOCK_SUPPORT], 1, [Define to 1 to include code that uses lvmlockd sanlock option.])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Look for sanlock libraries
|
||||
if test "$BUILD_LOCKDSANLOCK" = yes; then
|
||||
PKG_CHECK_MODULES(LOCKD_SANLOCK, libsanlock_client, [HAVE_LOCKD_SANLOCK=yes], $bailout)
|
||||
BUILD_LVMLOCKD=yes
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Build lockddlm
|
||||
AC_MSG_CHECKING(whether to build lockddlm)
|
||||
AC_ARG_ENABLE(lockd-dlm,
|
||||
AC_HELP_STRING([--enable-lockd-dlm],
|
||||
[enable the LVM lock daemon using dlm]),
|
||||
LOCKDDLM=$enableval)
|
||||
AC_MSG_RESULT($LOCKDDLM)
|
||||
|
||||
BUILD_LOCKDDLM=$LOCKDDLM
|
||||
|
||||
if test "$BUILD_LOCKDDLM" = yes; then
|
||||
AC_DEFINE([LOCKDDLM_SUPPORT], 1, [Define to 1 to include code that uses lvmlockd dlm option.])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Look for dlm libraries
|
||||
if test "$BUILD_LOCKDDLM" = yes; then
|
||||
PKG_CHECK_MODULES(LOCKD_DLM, libdlm, [HAVE_LOCKD_DLM=yes], $bailout)
|
||||
BUILD_LVMLOCKD=yes
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Build lvmlockd
|
||||
|
||||
AC_MSG_CHECKING(whether to build lvmlockd)
|
||||
AC_MSG_RESULT($BUILD_LVMLOCKD)
|
||||
|
||||
if test "$BUILD_LVMLOCKD" = yes; then
|
||||
AS_IF([test -n "$BUILD_LVMPOLLD"], [BUILD_LVMPOLLD=yes; AC_MSG_WARN([Enabling lvmpolld - required by lvmlockd.])])
|
||||
AS_IF([test -n "$BUILD_LVMETAD"], [BUILD_LVMETAD=yes; AC_MSG_WARN([Enabling lvmetad - required by lvmlockd.])])
|
||||
AC_MSG_CHECKING([defaults for use_lvmlockd])
|
||||
AC_ARG_ENABLE(use_lvmlockd,
|
||||
AC_HELP_STRING([--disable-use-lvmlockd],
|
||||
[disable usage of LVM lock daemon]),
|
||||
[case ${enableval} in
|
||||
yes) DEFAULT_USE_LVMLOCKD=1 ;;
|
||||
*) DEFAULT_USE_LVMLOCKD=0 ;;
|
||||
esac], DEFAULT_USE_LVMLOCKD=1)
|
||||
AC_MSG_RESULT($DEFAULT_USE_LVMLOCKD)
|
||||
AC_DEFINE([LVMLOCKD_SUPPORT], 1, [Define to 1 to include code that uses lvmlockd.])
|
||||
|
||||
AC_ARG_WITH(lvmlockd-pidfile,
|
||||
AC_HELP_STRING([--with-lvmlockd-pidfile=PATH],
|
||||
[lvmlockd pidfile [PID_DIR/lvmlockd.pid]]),
|
||||
LVMLOCKD_PIDFILE=$withval,
|
||||
LVMLOCKD_PIDFILE="$DEFAULT_PID_DIR/lvmlockd.pid")
|
||||
AC_DEFINE_UNQUOTED(LVMLOCKD_PIDFILE, ["$LVMLOCKD_PIDFILE"],
|
||||
[Path to lvmlockd pidfile.])
|
||||
else
|
||||
DEFAULT_USE_LVMLOCKD=0
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_USE_LVMLOCKD, [$DEFAULT_USE_LVMLOCKD],
|
||||
[Use lvmlockd by default.])
|
||||
|
||||
################################################################################
|
||||
dnl -- Check lvmetad
|
||||
if test "$BUILD_LVMETAD" = yes; then
|
||||
AC_MSG_CHECKING([defaults for use_lvmetad])
|
||||
AC_ARG_ENABLE(use_lvmetad,
|
||||
AC_HELP_STRING([--disable-use-lvmetad],
|
||||
[disable usage of LVM Metadata Daemon]),
|
||||
[case ${enableval} in
|
||||
yes) DEFAULT_USE_LVMETAD=1 ;;
|
||||
*) DEFAULT_USE_LVMETAD=0 ;;
|
||||
esac], DEFAULT_USE_LVMETAD=1)
|
||||
AC_MSG_RESULT($DEFAULT_USE_LVMETAD)
|
||||
AC_DEFINE([LVMETAD_SUPPORT], 1, [Define to 1 to include code that uses lvmetad.])
|
||||
|
||||
AC_ARG_WITH(lvmetad-pidfile,
|
||||
@@ -1071,9 +1233,41 @@ if test "$BUILD_LVMETAD" = yes; then
|
||||
LVMETAD_PIDFILE="$DEFAULT_PID_DIR/lvmetad.pid")
|
||||
AC_DEFINE_UNQUOTED(LVMETAD_PIDFILE, ["$LVMETAD_PIDFILE"],
|
||||
[Path to lvmetad pidfile.])
|
||||
else
|
||||
DEFAULT_USE_LVMETAD=0
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_USE_LVMETAD, [$DEFAULT_USE_LVMETAD],
|
||||
[Use lvmetad by default.])
|
||||
|
||||
################################################################################
|
||||
dnl -- Check lvmpolld
|
||||
if test "$BUILD_LVMPOLLD" = yes; then
|
||||
AC_MSG_CHECKING([defaults for use_lvmpolld])
|
||||
AC_ARG_ENABLE(use_lvmpolld,
|
||||
AC_HELP_STRING([--disable-use-lvmpolld],
|
||||
[disable usage of LVM Poll Daemon]),
|
||||
[case ${enableval} in
|
||||
yes) DEFAULT_USE_LVMPOLLD=1 ;;
|
||||
*) DEFAULT_USE_LVMPOLLD=0 ;;
|
||||
esac], DEFAULT_USE_LVMPOLLD=1)
|
||||
AC_MSG_RESULT($DEFAULT_USE_LVMPOLLD)
|
||||
AC_DEFINE([LVMPOLLD_SUPPORT], 1, [Define to 1 to include code that uses lvmpolld.])
|
||||
|
||||
AC_ARG_WITH(lvmpolld-pidfile,
|
||||
AC_HELP_STRING([--with-lvmpolld-pidfile=PATH],
|
||||
[lvmpolld pidfile [PID_DIR/lvmpolld.pid]]),
|
||||
LVMPOLLD_PIDFILE=$withval,
|
||||
LVMPOLLD_PIDFILE="$DEFAULT_PID_DIR/lvmpolld.pid")
|
||||
AC_DEFINE_UNQUOTED(LVMPOLLD_PIDFILE, ["$LVMPOLLD_PIDFILE"],
|
||||
[Path to lvmpolld pidfile.])
|
||||
else
|
||||
DEFAULT_USE_LVMPOLLD=0
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_USE_LVMPOLLD, [$DEFAULT_USE_LVMPOLLD],
|
||||
[Use lvmpolld by default.])
|
||||
|
||||
################################################################################
|
||||
|
||||
dnl -- Enable blkid wiping functionality
|
||||
AC_MSG_CHECKING(whether to enable libblkid detection of signatures when wiping)
|
||||
AC_ARG_ENABLE(blkid_wiping,
|
||||
@@ -1093,9 +1287,16 @@ if test "$BLKID_WIPING" != no; then
|
||||
fi])
|
||||
if test "$BLKID_WIPING" = yes; then
|
||||
BLKID_PC="blkid"
|
||||
DEFAULT_USE_BLKID_WIPING=1
|
||||
AC_DEFINE([BLKID_WIPING_SUPPORT], 1, [Define to 1 to use libblkid detection of signatures when wiping.])
|
||||
else
|
||||
DEFAULT_USE_BLKID_WIPING=1
|
||||
fi
|
||||
else
|
||||
DEFAULT_USE_BLKID_WIPING=0
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_USE_BLKID_WIPING, [$DEFAULT_USE_BLKID_WIPING],
|
||||
[Use blkid wiping by default.])
|
||||
|
||||
################################################################################
|
||||
dnl -- Enable udev-systemd protocol to instantiate a service for background jobs
|
||||
@@ -1168,11 +1369,11 @@ AC_ARG_ENABLE(compat,
|
||||
[enable support for old device-mapper versions]),
|
||||
DM_COMPAT=$enableval, DM_COMPAT=no)
|
||||
|
||||
if test "$DM_COMPAT" = yes; then
|
||||
AC_MSG_ERROR([--enable-compat is not currently supported.
|
||||
AS_IF([test "$DM_COMPAT" = yes],
|
||||
[AC_DEFINE([DM_COMPAT], 1, [Define to enable compat protocol])
|
||||
AC_MSG_ERROR([--enable-compat is not currently supported.
|
||||
Since device-mapper version 1.02.66, only one version (4) of the device-mapper
|
||||
ioctl protocol is supported.])
|
||||
fi
|
||||
ioctl protocol is supported.])])
|
||||
|
||||
################################################################################
|
||||
dnl -- Compatible units suffix mode
|
||||
@@ -1192,6 +1393,8 @@ AC_ARG_ENABLE(ioctl,
|
||||
AC_HELP_STRING([--disable-ioctl],
|
||||
[disable ioctl calls to device-mapper in the kernel]),
|
||||
DM_IOCTLS=$enableval)
|
||||
AS_IF([test "$DM_IOCTLS" = yes],
|
||||
[AC_DEFINE([DM_IOCTLS], 1, [Define to enable ioctls calls to kernel])])
|
||||
|
||||
################################################################################
|
||||
dnl -- Disable O_DIRECT
|
||||
@@ -1374,9 +1577,11 @@ if test "$REALTIME" = yes; then
|
||||
if test "$HAVE_REALTIME" = yes; then
|
||||
AC_DEFINE([HAVE_REALTIME], 1, [Define to 1 to include support for realtime clock.])
|
||||
LIBS="-lrt $LIBS"
|
||||
RT_PC="librt"
|
||||
else
|
||||
AC_MSG_WARN(Disabling realtime clock)
|
||||
fi
|
||||
AC_MSG_RESULT($HAVE_REALTIME)
|
||||
fi
|
||||
|
||||
dnl Check if the system has struct stat st_ctim.
|
||||
@@ -1450,14 +1655,16 @@ if test "$INTL" = yes; then
|
||||
# FIXME - Move this - can be device-mapper too
|
||||
INTL_PACKAGE="lvm2"
|
||||
AC_PATH_TOOL(MSGFMT, msgfmt)
|
||||
if [[ -z "$MSGFMT" ]]; then
|
||||
AC_MSG_ERROR([msgfmt not found in path $PATH])
|
||||
fi
|
||||
|
||||
AS_IF([test -z "$MSGFMT"], [AC_MSG_ERROR([msgfmt not found in path $PATH])])
|
||||
|
||||
AC_ARG_WITH(localedir,
|
||||
AC_HELP_STRING([--with-localedir=DIR],
|
||||
[translation files in DIR [PREFIX/share/locale]]),
|
||||
LOCALEDIR=$withval, LOCALEDIR='${prefix}/share/locale')
|
||||
[locale-dependent data [DATAROOTDIR/locale]]),
|
||||
localedir=$withval, localedir=${localedir-'${datarootdir}/locale'})
|
||||
AC_DEFINE_UNQUOTED([INTL_PACKAGE], ["$INTL_PACKAGE"], [Internalization package])
|
||||
# double eval needed ${datarootdir} -> ${prefix}/share -> real path
|
||||
AC_DEFINE_UNQUOTED([LOCALEDIR], ["$(eval echo $(eval echo $localedir))"], [Locale-dependent data])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
@@ -1522,6 +1729,19 @@ if test "$READLINE" = yes; then
|
||||
AC_CHECK_HEADERS(readline/readline.h readline/history.h,,hard_bailout)
|
||||
fi
|
||||
|
||||
if test "$BUILD_CMIRRORD" = yes; then
|
||||
AC_CHECK_FUNCS(atexit,,hard_bailout)
|
||||
fi
|
||||
|
||||
if test "$BUILD_LVMLOCKD" = yes; then
|
||||
AC_CHECK_FUNCS(clock_gettime strtoull,,hard_bailout)
|
||||
fi
|
||||
|
||||
if test "$BUILD_LVMPOLLD" = yes; then
|
||||
AC_CHECK_FUNCS(strpbrk,,hard_bailout)
|
||||
AC_FUNC_STRERROR_R
|
||||
fi
|
||||
|
||||
if test "$CLVMD" != none; then
|
||||
AC_CHECK_HEADERS(mntent.h netdb.h netinet/in.h pthread.h search.h sys/mount.h sys/socket.h sys/uio.h sys/un.h utmpx.h,,AC_MSG_ERROR(bailing out))
|
||||
AC_CHECK_FUNCS(dup2 getmntent memmove select socket,,hard_bailout)
|
||||
@@ -1679,10 +1899,13 @@ LVM_LIBAPI=`echo "$VER" | $AWK -F '[[()]]' '{print $2}'`
|
||||
AC_SUBST(APPLIB)
|
||||
AC_SUBST(AWK)
|
||||
AC_SUBST(BLKID_PC)
|
||||
AC_SUBST(BLKID_WIPING)
|
||||
AC_SUBST(BUILD_CMIRRORD)
|
||||
AC_SUBST(BUILD_DMEVENTD)
|
||||
AC_SUBST(BUILD_LVMETAD)
|
||||
AC_SUBST(BUILD_LVMPOLLD)
|
||||
AC_SUBST(BUILD_LVMLOCKD)
|
||||
AC_SUBST(BUILD_LOCKDSANLOCK)
|
||||
AC_SUBST(BUILD_LOCKDDLM)
|
||||
AC_SUBST(CACHE)
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(CFLOW_CMD)
|
||||
@@ -1712,6 +1935,7 @@ AC_SUBST(DEFAULT_CACHE_SUBDIR)
|
||||
AC_SUBST(DEFAULT_DATA_ALIGNMENT)
|
||||
AC_SUBST(DEFAULT_DM_RUN_DIR)
|
||||
AC_SUBST(DEFAULT_LOCK_DIR)
|
||||
AC_SUBST(DEFAULT_FALLBACK_TO_LVM1)
|
||||
AC_SUBST(DEFAULT_MIRROR_SEGTYPE)
|
||||
AC_SUBST(DEFAULT_PID_DIR)
|
||||
AC_SUBST(DEFAULT_PROFILE_SUBDIR)
|
||||
@@ -1719,17 +1943,16 @@ AC_SUBST(DEFAULT_RAID10_SEGTYPE)
|
||||
AC_SUBST(DEFAULT_RUN_DIR)
|
||||
AC_SUBST(DEFAULT_SPARSE_SEGTYPE)
|
||||
AC_SUBST(DEFAULT_SYS_DIR)
|
||||
AC_SUBST(DEFAULT_USE_BLKID_WIPING)
|
||||
AC_SUBST(DEFAULT_USE_LVMETAD)
|
||||
AC_SUBST(DEFAULT_USE_LVMPOLLD)
|
||||
AC_SUBST(DEFAULT_USE_LVMLOCKD)
|
||||
AC_SUBST(DEVMAPPER)
|
||||
AC_SUBST(DLM_CFLAGS)
|
||||
AC_SUBST(DLM_LIBS)
|
||||
AC_SUBST(DL_LIBS)
|
||||
AC_SUBST(DMEVENTD)
|
||||
AC_SUBST(DMEVENTD_PATH)
|
||||
AC_SUBST(DM_COMPAT)
|
||||
AC_SUBST(DM_DEVICE_GID)
|
||||
AC_SUBST(DM_DEVICE_MODE)
|
||||
AC_SUBST(DM_DEVICE_UID)
|
||||
AC_SUBST(DM_IOCTLS)
|
||||
AC_SUBST(DM_LIB_VERSION)
|
||||
AC_SUBST(DM_LIB_PATCHLEVEL)
|
||||
AC_SUBST(ELDFLAGS)
|
||||
@@ -1739,12 +1962,10 @@ AC_SUBST(HAVE_LIBDL)
|
||||
AC_SUBST(HAVE_REALTIME)
|
||||
AC_SUBST(HAVE_VALGRIND)
|
||||
AC_SUBST(INTL)
|
||||
AC_SUBST(INTL_PACKAGE)
|
||||
AC_SUBST(JOBS)
|
||||
AC_SUBST(LDDEPS)
|
||||
AC_SUBST(LIBS)
|
||||
AC_SUBST(LIB_SUFFIX)
|
||||
AC_SUBST(LOCALEDIR)
|
||||
AC_SUBST(LVM1)
|
||||
AC_SUBST(LVM1_FALLBACK)
|
||||
AC_SUBST(LVM_VERSION)
|
||||
@@ -1755,6 +1976,7 @@ AC_SUBST(LVM_PATCHLEVEL)
|
||||
AC_SUBST(LVM_PATH)
|
||||
AC_SUBST(LVM_RELEASE)
|
||||
AC_SUBST(LVM_RELEASE_DATE)
|
||||
AC_SUBST(localedir)
|
||||
AC_SUBST(MANGLING)
|
||||
AC_SUBST(MIRRORS)
|
||||
AC_SUBST(MSGFMT)
|
||||
@@ -1770,6 +1992,7 @@ AC_SUBST(PYTHON_LIBDIRS)
|
||||
AC_SUBST(QUORUM_CFLAGS)
|
||||
AC_SUBST(QUORUM_LIBS)
|
||||
AC_SUBST(RAID)
|
||||
AC_SUBST(RT_PC)
|
||||
AC_SUBST(READLINE_LIBS)
|
||||
AC_SUBST(REPLICATORS)
|
||||
AC_SUBST(SACKPT_CFLAGS)
|
||||
@@ -1782,6 +2005,7 @@ AC_SUBST(SNAPSHOTS)
|
||||
AC_SUBST(STATICDIR)
|
||||
AC_SUBST(STATIC_LINK)
|
||||
AC_SUBST(TESTING)
|
||||
AC_SUBST(TESTSUITE_DATA)
|
||||
AC_SUBST(THIN)
|
||||
AC_SUBST(THIN_CHECK_CMD)
|
||||
AC_SUBST(THIN_DUMP_CMD)
|
||||
@@ -1801,6 +2025,8 @@ AC_SUBST(VALGRIND_POOL)
|
||||
AC_SUBST(WRITE_INSTALL)
|
||||
AC_SUBST(DMEVENTD_PIDFILE)
|
||||
AC_SUBST(LVMETAD_PIDFILE)
|
||||
AC_SUBST(LVMPOLLD_PIDFILE)
|
||||
AC_SUBST(LVMLOCKD_PIDFILE)
|
||||
AC_SUBST(CLVMD_PIDFILE)
|
||||
AC_SUBST(CMIRRORD_PIDFILE)
|
||||
AC_SUBST(interface)
|
||||
@@ -1834,6 +2060,8 @@ daemons/dmeventd/plugins/mirror/Makefile
|
||||
daemons/dmeventd/plugins/snapshot/Makefile
|
||||
daemons/dmeventd/plugins/thin/Makefile
|
||||
daemons/lvmetad/Makefile
|
||||
daemons/lvmpolld/Makefile
|
||||
daemons/lvmlockd/Makefile
|
||||
conf/Makefile
|
||||
conf/example.conf
|
||||
conf/lvmlocal.conf
|
||||
@@ -1877,6 +2105,11 @@ scripts/lvm2_cmirrord_systemd_red_hat.service
|
||||
scripts/lvm2_lvmetad_init_red_hat
|
||||
scripts/lvm2_lvmetad_systemd_red_hat.service
|
||||
scripts/lvm2_lvmetad_systemd_red_hat.socket
|
||||
scripts/lvm2_lvmpolld_init_red_hat
|
||||
scripts/lvm2_lvmpolld_systemd_red_hat.service
|
||||
scripts/lvm2_lvmpolld_systemd_red_hat.socket
|
||||
scripts/lvm2_lvmlockd_systemd_red_hat.service
|
||||
scripts/lvm2_lvmlocking_systemd_red_hat.service
|
||||
scripts/lvm2_monitoring_init_red_hat
|
||||
scripts/lvm2_monitoring_systemd_red_hat.service
|
||||
scripts/lvm2_pvscan_systemd_red_hat@.service
|
||||
@@ -1893,10 +2126,14 @@ unit-tests/mm/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
test -n "$THIN_CONFIGURE_WARN" && AC_MSG_WARN([Support for thin provisioning is limited since some thin provisioning tools are missing!])
|
||||
AS_IF([test -n "$THIN_CONFIGURE_WARN"],
|
||||
[AC_MSG_WARN([Support for thin provisioning is limited since some thin provisioning tools are missing!])])
|
||||
|
||||
test -n "$THIN_CHECK_VERSION_WARN" && AC_MSG_WARN([You should also install thin_check vsn 0.3.2 (or later) to use lvm2 thin provisioning])
|
||||
AS_IF([test -n "$THIN_CHECK_VERSION_WARN"],
|
||||
[AC_MSG_WARN([You should also install thin_check vsn 0.3.2 (or later) to use lvm2 thin provisioning])])
|
||||
|
||||
test -n "$CACHE_CONFIGURE_WARN" && AC_MSG_WARN([Support for cache is limited since some cache tools are missing!])
|
||||
AS_IF([test -n "$CACHE_CONFIGURE_WARN"],
|
||||
[AC_MSG_WARN([Support for cache is limited since some cache tools are missing!])])
|
||||
|
||||
test "$ODIRECT" = yes || AC_MSG_WARN([O_DIRECT disabled: low-memory pvmove may lock up])
|
||||
AS_IF([test "$ODIRECT" != yes],
|
||||
[AC_MSG_WARN([O_DIRECT disabled: low-memory pvmove may lock up])])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@@ -15,7 +15,7 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
.PHONY: dmeventd clvmd cmirrord lvmetad
|
||||
.PHONY: dmeventd clvmd cmirrord lvmetad lvmpolld lvmlockd
|
||||
|
||||
ifneq ("@CLVMD@", "none")
|
||||
SUBDIRS += clvmd
|
||||
@@ -36,8 +36,16 @@ ifeq ("@BUILD_LVMETAD@", "yes")
|
||||
SUBDIRS += lvmetad
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LVMPOLLD@", "yes")
|
||||
SUBDIRS += lvmpolld
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LVMLOCKD@", "yes")
|
||||
SUBDIRS += lvmlockd
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad
|
||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd
|
||||
endif
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
1
daemons/clvmd/.gitignore
vendored
Normal file
1
daemons/clvmd/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
clvmd
|
||||
@@ -36,10 +36,6 @@ SOURCES = \
|
||||
lvm-functions.c \
|
||||
refresh_clvmd.c
|
||||
|
||||
ifeq ("@DEBUG@", "yes")
|
||||
DEFS += -DDEBUG
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring cman,, "@CLVMD@,"))
|
||||
SOURCES += clvmd-cman.c
|
||||
LMLIBS += $(CMAN_LIBS) $(CONFDB_LIBS) $(DLM_LIBS)
|
||||
|
||||
@@ -323,6 +323,7 @@ void cmd_client_cleanup(struct local_client *client)
|
||||
int lkid;
|
||||
char *lockname;
|
||||
|
||||
DEBUGLOG("Client thread cleanup (%p)\n", client);
|
||||
if (!client->bits.localsock.private)
|
||||
return;
|
||||
|
||||
@@ -331,7 +332,7 @@ void cmd_client_cleanup(struct local_client *client)
|
||||
dm_hash_iterate(v, lock_hash) {
|
||||
lkid = (int)(long)dm_hash_get_data(lock_hash, v);
|
||||
lockname = dm_hash_get_key(lock_hash, v);
|
||||
DEBUGLOG("cleanup: Unlocking lock %s %x\n", lockname, lkid);
|
||||
DEBUGLOG("Cleanup (%p): Unlocking lock %s %x\n", client, lockname, lkid);
|
||||
(void) sync_unlock(lockname, lkid);
|
||||
}
|
||||
|
||||
@@ -339,7 +340,6 @@ void cmd_client_cleanup(struct local_client *client)
|
||||
client->bits.localsock.private = NULL;
|
||||
}
|
||||
|
||||
|
||||
static int restart_clvmd(void)
|
||||
{
|
||||
const char **argv;
|
||||
|
||||
@@ -18,15 +18,10 @@
|
||||
#ifndef _LVM_CLVMD_COMMON_H
|
||||
#define _LVM_CLVMD_COMMON_H
|
||||
|
||||
#include "configure.h"
|
||||
|
||||
#define _REENTRANT
|
||||
#define _GNU_SOURCE
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "libdevmapper.h"
|
||||
#include "tool.h"
|
||||
|
||||
#include "lvm-logging.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -243,7 +243,7 @@ static void openais_cpg_confchg_callback(cpg_handle_t handle,
|
||||
struct node_info *ninfo;
|
||||
|
||||
DEBUGLOG("confchg callback. %" PRIsize_t " joined, "
|
||||
"%" PRIsize_t " left, %" PRIsize_t " members\n",
|
||||
FMTsize_t " left, %" PRIsize_t " members\n",
|
||||
joined_list_entries, left_list_entries, member_list_entries);
|
||||
|
||||
for (i=0; i<joined_list_entries; i++) {
|
||||
|
||||
@@ -172,6 +172,7 @@ static void usage(const char *prog, FILE *file)
|
||||
#ifdef USE_SINGLENODE
|
||||
"singlenode "
|
||||
#endif
|
||||
"\n"
|
||||
" -R Tell all running clvmds in the cluster to reload their device cache\n"
|
||||
" -S Restart clvmd, preserving exclusive locks\n"
|
||||
" -t<secs> Command timeout (default: 60 seconds)\n"
|
||||
@@ -222,6 +223,7 @@ void debuglog(const char *fmt, ...)
|
||||
fprintf(stderr, "CLVMD[%x]: %.15s ", (int)pthread_self(), ctime_r(&P, buf_ctime) + 4);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fflush(stderr);
|
||||
break;
|
||||
case DEBUG_SYSLOG:
|
||||
if (!syslog_init) {
|
||||
@@ -597,7 +599,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* This needs to be started after cluster initialisation
|
||||
as it may need to take out locks */
|
||||
DEBUGLOG("starting LVM thread\n");
|
||||
DEBUGLOG("Starting LVM thread\n");
|
||||
DEBUGLOG("Main cluster socket fd %d (%p) with local socket %d (%p)\n",
|
||||
local_client_head.fd, &local_client_head, newfd->fd, newfd);
|
||||
|
||||
/* Don't let anyone else to do work until we are started */
|
||||
pthread_create(&lvm_thread, &stack_attr, lvm_thread_fn, &lvm_params);
|
||||
@@ -697,7 +701,7 @@ static int local_rendezvous_callback(struct local_client *thisfd, char *buf,
|
||||
newfd->type = LOCAL_SOCK;
|
||||
newfd->callback = local_sock_callback;
|
||||
newfd->bits.localsock.all_success = 1;
|
||||
DEBUGLOG("Got new connection on fd %d\n", newfd->fd);
|
||||
DEBUGLOG("Got new connection on fd %d (%p)\n", newfd->fd, newfd);
|
||||
*new_client = newfd;
|
||||
}
|
||||
return 1;
|
||||
@@ -849,18 +853,48 @@ static void main_loop(int cmd_timeout)
|
||||
struct local_client *thisfd;
|
||||
struct timeval tv = { cmd_timeout, 0 };
|
||||
int quorate = clops->is_quorate();
|
||||
int client_count = 0;
|
||||
int max_fd = 0;
|
||||
|
||||
/* Wait on the cluster FD and all local sockets/pipes */
|
||||
local_client_head.fd = clops->get_main_cluster_fd();
|
||||
FD_ZERO(&in);
|
||||
struct local_client *lastfd = &local_client_head;
|
||||
struct local_client *nextfd = local_client_head.next;
|
||||
|
||||
for (thisfd = &local_client_head; thisfd; thisfd = thisfd->next) {
|
||||
client_count++;
|
||||
max_fd = max(max_fd, thisfd->fd);
|
||||
}
|
||||
|
||||
if (max_fd > FD_SETSIZE - 32) {
|
||||
fprintf(stderr, "WARNING: There are too many connections to clvmd. Investigate and take action now!\n");
|
||||
fprintf(stderr, "WARNING: Your cluster may freeze up if the number of clvmd file descriptors (%d) exceeds %d.\n", max_fd + 1, FD_SETSIZE);
|
||||
}
|
||||
|
||||
for (thisfd = &local_client_head; thisfd; thisfd = nextfd, nextfd = thisfd ? thisfd->next : NULL) {
|
||||
|
||||
if (thisfd->removeme && !cleanup_zombie(thisfd)) {
|
||||
struct local_client *free_fd = thisfd;
|
||||
lastfd->next = nextfd;
|
||||
DEBUGLOG("removeme set for %p with %d monitored fds remaining\n", free_fd, client_count - 1);
|
||||
|
||||
/* Queue cleanup, this also frees the client struct */
|
||||
add_to_lvmqueue(free_fd, NULL, 0, NULL);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
lastfd = thisfd;
|
||||
|
||||
if (thisfd->removeme)
|
||||
continue;
|
||||
|
||||
/* if the cluster is not quorate then don't listen for new requests */
|
||||
if ((thisfd->type != LOCAL_RENDEZVOUS &&
|
||||
thisfd->type != LOCAL_SOCK) || quorate)
|
||||
FD_SET(thisfd->fd, &in);
|
||||
if (thisfd->fd < FD_SETSIZE)
|
||||
FD_SET(thisfd->fd, &in);
|
||||
}
|
||||
|
||||
select_status = select(FD_SETSIZE, &in, NULL, NULL, &tv);
|
||||
@@ -876,31 +910,20 @@ static void main_loop(int cmd_timeout)
|
||||
}
|
||||
|
||||
if (select_status > 0) {
|
||||
struct local_client *lastfd = NULL;
|
||||
char csid[MAX_CSID_LEN];
|
||||
char buf[max_cluster_message];
|
||||
|
||||
for (thisfd = &local_client_head; thisfd; thisfd = thisfd->next) {
|
||||
if (thisfd->removeme && !cleanup_zombie(thisfd)) {
|
||||
struct local_client *free_fd = thisfd;
|
||||
lastfd->next = thisfd->next;
|
||||
DEBUGLOG("removeme set for fd %d\n", free_fd->fd);
|
||||
|
||||
/* Queue cleanup, this also frees the client struct */
|
||||
add_to_lvmqueue(free_fd, NULL, 0, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET(thisfd->fd, &in)) {
|
||||
if (thisfd->fd < FD_SETSIZE && FD_ISSET(thisfd->fd, &in)) {
|
||||
struct local_client *newfd = NULL;
|
||||
int ret;
|
||||
|
||||
/* FIXME Remove from main thread in case it blocks! */
|
||||
/* Do callback */
|
||||
ret = thisfd->callback(thisfd, buf, sizeof(buf),
|
||||
csid, &newfd);
|
||||
/* Ignore EAGAIN */
|
||||
if (ret < 0 && (errno == EAGAIN || errno == EINTR)) {
|
||||
lastfd = thisfd;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -916,17 +939,16 @@ static void main_loop(int cmd_timeout)
|
||||
DEBUGLOG("ret == %d, errno = %d. removing client\n",
|
||||
ret, errno);
|
||||
thisfd->removeme = 1;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* New client...simply add it to the list */
|
||||
if (newfd) {
|
||||
newfd->next = thisfd->next;
|
||||
thisfd->next = newfd;
|
||||
break;
|
||||
thisfd = newfd;
|
||||
}
|
||||
}
|
||||
lastfd = thisfd;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1419,7 +1441,7 @@ static int read_from_local_sock(struct local_client *thisfd)
|
||||
thisfd->bits.localsock.in_progress = TRUE;
|
||||
thisfd->bits.localsock.state = PRE_COMMAND;
|
||||
thisfd->bits.localsock.cleanup_needed = 1;
|
||||
DEBUGLOG("Creating pre&post thread\n");
|
||||
DEBUGLOG("Creating pre&post thread for pipe fd %d (%p)\n", newfd->fd, newfd);
|
||||
status = pthread_create(&thisfd->bits.localsock.threadid,
|
||||
&stack_attr, pre_and_post_thread, thisfd);
|
||||
DEBUGLOG("Created pre&post thread, state = %d\n", status);
|
||||
@@ -1673,7 +1695,7 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
|
||||
sigset_t ss;
|
||||
int pipe_fd = client->bits.localsock.pipe;
|
||||
|
||||
DEBUGLOG("Pre&post thread (%p), pipe %d\n", client, pipe_fd);
|
||||
DEBUGLOG("Pre&post thread (%p), pipe fd %d\n", client, pipe_fd);
|
||||
pthread_mutex_lock(&client->bits.localsock.mutex);
|
||||
|
||||
/* Ignore SIGUSR1 (handled by master process) but enable
|
||||
@@ -1693,7 +1715,7 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
|
||||
if ((status = do_pre_command(client)))
|
||||
client->bits.localsock.all_success = 0;
|
||||
|
||||
DEBUGLOG("Pre&post thread (%p) writes status %d down to pipe %d\n",
|
||||
DEBUGLOG("Pre&post thread (%p) writes status %d down to pipe fd %d\n",
|
||||
client, status, pipe_fd);
|
||||
|
||||
/* Tell the parent process we have finished this bit */
|
||||
@@ -1975,7 +1997,7 @@ static int process_work_item(struct lvm_thread_cmd *cmd)
|
||||
{
|
||||
/* If msg is NULL then this is a cleanup request */
|
||||
if (cmd->msg == NULL) {
|
||||
DEBUGLOG("process_work_item: free fd %d\n", cmd->client->fd);
|
||||
DEBUGLOG("process_work_item: free %p\n", cmd->client);
|
||||
cmd_client_cleanup(cmd->client);
|
||||
pthread_mutex_destroy(&cmd->client->bits.localsock.mutex);
|
||||
pthread_cond_destroy(&cmd->client->bits.localsock.cond);
|
||||
|
||||
@@ -510,7 +510,7 @@ int do_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
|
||||
DEBUGLOG("do_lock_lv: resource '%s', cmd = %s, flags = %s, critical_section = %d\n",
|
||||
resource, decode_locking_cmd(command), decode_flags(lock_flags), critical_section());
|
||||
|
||||
if (!cmd->config_initialized || config_files_changed(cmd)) {
|
||||
if (!cmd->initialized.config || config_files_changed(cmd)) {
|
||||
/* Reinitialise various settings inc. logging, filters */
|
||||
if (do_refresh_cache()) {
|
||||
log_error("Updated config file invalid. Aborting.");
|
||||
@@ -899,7 +899,7 @@ int init_clvm(struct dm_hash_table *excl_uuid)
|
||||
if (!get_initial_state(excl_uuid))
|
||||
log_error("Cannot load initial lock states.");
|
||||
|
||||
if (!(cmd = create_toolcontext(1, NULL, 0, 1))) {
|
||||
if (!(cmd = create_toolcontext(1, NULL, 0, 1, 1, 1))) {
|
||||
log_error("Failed to allocate command context");
|
||||
return 0;
|
||||
}
|
||||
|
||||
1
daemons/cmirrord/.gitignore
vendored
Normal file
1
daemons/cmirrord/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
cmirrord
|
||||
1
daemons/dmeventd/.gitignore
vendored
Normal file
1
daemons/dmeventd/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
dmeventd
|
||||
@@ -16,26 +16,21 @@
|
||||
* dmeventd - dm event daemon to monitor active mapped devices
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#include "tool.h"
|
||||
|
||||
#include "configure.h"
|
||||
#include "libdevmapper.h"
|
||||
#include "libdevmapper-event.h"
|
||||
#include "dmeventd.h"
|
||||
//#include "libmultilog.h"
|
||||
#include "dm-logging.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "libdevmapper-event.h"
|
||||
#include "dmeventd.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <arpa/inet.h> /* for htonl, ntohl */
|
||||
#include <fcntl.h> /* for musl libc */
|
||||
@@ -133,51 +128,20 @@ void debuglog(const char *fmt, ...)
|
||||
|
||||
static const char *decode_cmd(uint32_t cmd)
|
||||
{
|
||||
static char buf[128];
|
||||
const char *command;
|
||||
|
||||
switch (cmd) {
|
||||
case DM_EVENT_CMD_ACTIVE:
|
||||
command = "ACTIVE";
|
||||
break;
|
||||
case DM_EVENT_CMD_REGISTER_FOR_EVENT:
|
||||
command = "REGISTER_FOR_EVENT";
|
||||
break;
|
||||
case DM_EVENT_CMD_UNREGISTER_FOR_EVENT:
|
||||
command = "UNREGISTER_FOR_EVENT";
|
||||
break;
|
||||
case DM_EVENT_CMD_GET_REGISTERED_DEVICE:
|
||||
command = "GET_REGISTERED_DEVICE";
|
||||
break;
|
||||
case DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE:
|
||||
command = "GET_NEXT_REGISTERED_DEVICE";
|
||||
break;
|
||||
case DM_EVENT_CMD_SET_TIMEOUT:
|
||||
command = "SET_TIMEOUT";
|
||||
break;
|
||||
case DM_EVENT_CMD_GET_TIMEOUT:
|
||||
command = "GET_TIMEOUT";
|
||||
break;
|
||||
case DM_EVENT_CMD_HELLO:
|
||||
command = "HELLO";
|
||||
break;
|
||||
case DM_EVENT_CMD_DIE:
|
||||
command = "DIE";
|
||||
break;
|
||||
case DM_EVENT_CMD_GET_STATUS:
|
||||
command = "GET_STATUS";
|
||||
break;
|
||||
case DM_EVENT_CMD_GET_PARAMETERS:
|
||||
command = "GET_PARAMETERS";
|
||||
break;
|
||||
default:
|
||||
command = "unknown";
|
||||
break;
|
||||
case DM_EVENT_CMD_ACTIVE: return "ACTIVE";
|
||||
case DM_EVENT_CMD_REGISTER_FOR_EVENT: return "REGISTER_FOR_EVENT";
|
||||
case DM_EVENT_CMD_UNREGISTER_FOR_EVENT: return "UNREGISTER_FOR_EVENT";
|
||||
case DM_EVENT_CMD_GET_REGISTERED_DEVICE: return "GET_REGISTERED_DEVICE";
|
||||
case DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE: return "GET_NEXT_REGISTERED_DEVICE";
|
||||
case DM_EVENT_CMD_SET_TIMEOUT: return "SET_TIMEOUT";
|
||||
case DM_EVENT_CMD_GET_TIMEOUT: return "GET_TIMEOUT";
|
||||
case DM_EVENT_CMD_HELLO: return "HELLO";
|
||||
case DM_EVENT_CMD_DIE: return "DIE";
|
||||
case DM_EVENT_CMD_GET_STATUS: return "GET_STATUS";
|
||||
case DM_EVENT_CMD_GET_PARAMETERS: return "GET_PARAMETERS";
|
||||
default: return "unknown";
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s (0x%x)", command, cmd);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -710,6 +674,7 @@ static int _event_wait(struct thread_status *thread, struct dm_task **task)
|
||||
int ret = DM_WAIT_RETRY;
|
||||
struct dm_task *dmt;
|
||||
struct dm_info info;
|
||||
int ioctl_errno;
|
||||
|
||||
*task = 0;
|
||||
|
||||
@@ -739,25 +704,27 @@ static int _event_wait(struct thread_status *thread, struct dm_task **task)
|
||||
* either for a timeout event, or to cancel the thread.
|
||||
*/
|
||||
set = _unblock_sigalrm();
|
||||
errno = 0;
|
||||
if (dm_task_run(dmt)) {
|
||||
thread->current_events |= DM_EVENT_DEVICE_ERROR;
|
||||
ret = DM_WAIT_INTR;
|
||||
|
||||
if ((ret = dm_task_get_info(dmt, &info)))
|
||||
thread->event_nr = info.event_nr;
|
||||
} else if (thread->events & DM_EVENT_TIMEOUT && errno == EINTR) {
|
||||
thread->current_events |= DM_EVENT_TIMEOUT;
|
||||
ret = DM_WAIT_INTR;
|
||||
} else if (thread->status == DM_THREAD_SHUTDOWN && errno == EINTR) {
|
||||
ret = DM_WAIT_FATAL;
|
||||
} else {
|
||||
syslog(LOG_NOTICE, "dm_task_run failed, errno = %d, %s",
|
||||
errno, strerror(errno));
|
||||
if (errno == ENXIO) {
|
||||
syslog(LOG_ERR, "%s disappeared, detaching",
|
||||
thread->device.name);
|
||||
ioctl_errno = dm_task_get_errno(dmt);
|
||||
if (thread->events & DM_EVENT_TIMEOUT && ioctl_errno == EINTR) {
|
||||
thread->current_events |= DM_EVENT_TIMEOUT;
|
||||
ret = DM_WAIT_INTR;
|
||||
} else if (thread->status == DM_THREAD_SHUTDOWN && ioctl_errno == EINTR)
|
||||
ret = DM_WAIT_FATAL;
|
||||
else {
|
||||
syslog(LOG_NOTICE, "dm_task_run failed, errno = %d, %s",
|
||||
ioctl_errno, strerror(ioctl_errno));
|
||||
if (ioctl_errno == ENXIO) {
|
||||
syslog(LOG_ERR, "%s disappeared, detaching",
|
||||
thread->device.name);
|
||||
ret = DM_WAIT_FATAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUGLOG("Completed waitevent task for %s", thread->device.uuid);
|
||||
@@ -1595,9 +1562,6 @@ static void _process_request(struct dm_event_fifos *fifos)
|
||||
{
|
||||
int die;
|
||||
struct dm_event_daemon_message msg = { 0 };
|
||||
#ifdef DEBUG
|
||||
const char *cmd;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Read the request from the client (client_read, client_write
|
||||
@@ -1606,7 +1570,8 @@ static void _process_request(struct dm_event_fifos *fifos)
|
||||
if (!_client_read(fifos, &msg))
|
||||
return;
|
||||
|
||||
DEBUGLOG("%s processing...", cmd = decode_cmd(msg.cmd));
|
||||
DEBUGLOG("%s (0x%x) processing...", decode_cmd(msg.cmd), msg.cmd);
|
||||
|
||||
die = (msg.cmd == DM_EVENT_CMD_DIE) ? 1 : 0;
|
||||
|
||||
/* _do_process_request fills in msg (if memory allows for
|
||||
@@ -1618,7 +1583,7 @@ static void _process_request(struct dm_event_fifos *fifos)
|
||||
|
||||
dm_free(msg.data);
|
||||
|
||||
DEBUGLOG("%s completed.", cmd);
|
||||
DEBUGLOG("%s (0x%x) completed.", decode_cmd(msg.cmd), msg.cmd);
|
||||
|
||||
if (die) {
|
||||
if (unlink(DMEVENTD_PIDFILE))
|
||||
@@ -1668,10 +1633,8 @@ static void _cleanup_unused_threads(void)
|
||||
if (ret == ESRCH) {
|
||||
thread->status = DM_THREAD_DONE;
|
||||
} else if (ret) {
|
||||
syslog(LOG_ERR,
|
||||
"Unable to terminate thread: %s\n",
|
||||
strerror(-ret));
|
||||
stack;
|
||||
syslog(LOG_ERR, "Unable to terminate thread: %s",
|
||||
strerror(ret));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1703,8 +1666,7 @@ static void _cleanup_unused_threads(void)
|
||||
|
||||
static void _sig_alarm(int signum __attribute__((unused)))
|
||||
{
|
||||
DEBUGLOG("Received SIGALRM.");
|
||||
pthread_testcancel();
|
||||
/* empty SIG_IGN */;
|
||||
}
|
||||
|
||||
/* Init thread signal handling. */
|
||||
|
||||
@@ -17,15 +17,10 @@
|
||||
//#include "libmultilog.h"
|
||||
#include "dmeventd.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <arpa/inet.h> /* for htonl, ntohl */
|
||||
|
||||
|
||||
2
daemons/lvmetad/.gitignore
vendored
Normal file
2
daemons/lvmetad/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
lvmetad
|
||||
lvmetactl
|
||||
@@ -18,7 +18,7 @@ top_builddir = @top_builddir@
|
||||
SOURCES = lvmetad-core.c
|
||||
SOURCES2 = testclient.c
|
||||
|
||||
TARGETS = lvmetad lvmetad-testclient
|
||||
TARGETS = lvmetad lvmetactl
|
||||
|
||||
.PHONY: install_lvmetad
|
||||
|
||||
@@ -39,8 +39,11 @@ CFLAGS += $(EXTRA_EXEC_CFLAGS)
|
||||
|
||||
lvmetad: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
|
||||
$(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS)
|
||||
|
||||
lvmetactl: lvmetactl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmetactl.o $(LVMLIBS)
|
||||
|
||||
# TODO: No idea. No idea how to test either.
|
||||
#ifneq ("$(CFLOW_CMD)", "")
|
||||
|
||||
183
daemons/lvmetad/lvmetactl.c
Normal file
183
daemons/lvmetad/lvmetactl.c
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "tool.h"
|
||||
|
||||
#include "lvmetad-client.h"
|
||||
|
||||
daemon_handle h;
|
||||
|
||||
static void print_reply(daemon_reply reply)
|
||||
{
|
||||
const char *a = daemon_reply_str(reply, "response", NULL);
|
||||
const char *b = daemon_reply_str(reply, "status", NULL);
|
||||
const char *c = daemon_reply_str(reply, "reason", NULL);
|
||||
|
||||
printf("response \"%s\" status \"%s\" reason \"%s\"\n",
|
||||
a ? a : "", b ? b : "", c ? c : "");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
daemon_reply reply;
|
||||
char *cmd;
|
||||
char *uuid;
|
||||
char *name;
|
||||
int val;
|
||||
int ver;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("lvmeta dump\n");
|
||||
printf("lvmeta pv_list\n");
|
||||
printf("lvmeta vg_list\n");
|
||||
printf("lvmeta vg_lookup_name <name>\n");
|
||||
printf("lvmeta vg_lookup_uuid <uuid>\n");
|
||||
printf("lvmeta pv_lookup_uuid <uuid>\n");
|
||||
printf("lvmeta set_global_invalid 0|1\n");
|
||||
printf("lvmeta get_global_invalid\n");
|
||||
printf("lvmeta set_vg_version <uuid> <version>\n");
|
||||
printf("lvmeta vg_lock_type <uuid>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd = argv[1];
|
||||
|
||||
h = lvmetad_open(NULL);
|
||||
|
||||
if (!strcmp(cmd, "dump")) {
|
||||
reply = daemon_send_simple(h, "dump",
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else if (!strcmp(cmd, "pv_list")) {
|
||||
reply = daemon_send_simple(h, "pv_list",
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else if (!strcmp(cmd, "vg_list")) {
|
||||
reply = daemon_send_simple(h, "vg_list",
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else if (!strcmp(cmd, "set_global_invalid")) {
|
||||
if (argc < 3) {
|
||||
printf("set_global_invalid 0|1\n");
|
||||
return -1;
|
||||
}
|
||||
val = atoi(argv[2]);
|
||||
|
||||
reply = daemon_send_simple(h, "set_global_info",
|
||||
"global_invalid = %d", val,
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
print_reply(reply);
|
||||
|
||||
} else if (!strcmp(cmd, "get_global_invalid")) {
|
||||
reply = daemon_send_simple(h, "get_global_info",
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else if (!strcmp(cmd, "set_vg_version")) {
|
||||
if (argc < 4) {
|
||||
printf("set_vg_version <uuid> <ver>\n");
|
||||
return -1;
|
||||
}
|
||||
uuid = argv[2];
|
||||
ver = atoi(argv[3]);
|
||||
|
||||
reply = daemon_send_simple(h, "set_vg_info",
|
||||
"uuid = %s", uuid,
|
||||
"version = %d", ver,
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
print_reply(reply);
|
||||
|
||||
} else if (!strcmp(cmd, "vg_lookup_name")) {
|
||||
if (argc < 3) {
|
||||
printf("vg_lookup_name <name>\n");
|
||||
return -1;
|
||||
}
|
||||
name = argv[2];
|
||||
|
||||
reply = daemon_send_simple(h, "vg_lookup",
|
||||
"name = %s", name,
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else if (!strcmp(cmd, "vg_lookup_uuid")) {
|
||||
if (argc < 3) {
|
||||
printf("vg_lookup_uuid <uuid>\n");
|
||||
return -1;
|
||||
}
|
||||
uuid = argv[2];
|
||||
|
||||
reply = daemon_send_simple(h, "vg_lookup",
|
||||
"uuid = %s", uuid,
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else if (!strcmp(cmd, "vg_lock_type")) {
|
||||
struct dm_config_node *metadata;
|
||||
const char *lock_type;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("vg_lock_type <uuid>\n");
|
||||
return -1;
|
||||
}
|
||||
uuid = argv[2];
|
||||
|
||||
reply = daemon_send_simple(h, "vg_lookup",
|
||||
"uuid = %s", uuid,
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
/* printf("%s\n", reply.buffer.mem); */
|
||||
|
||||
metadata = dm_config_find_node(reply.cft->root, "metadata");
|
||||
if (!metadata) {
|
||||
printf("no metadata\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
lock_type = dm_config_find_str(metadata, "metadata/lock_type", NULL);
|
||||
if (!lock_type) {
|
||||
printf("no lock_type\n");
|
||||
goto out;
|
||||
}
|
||||
printf("lock_type %s\n", lock_type);
|
||||
|
||||
} else if (!strcmp(cmd, "pv_lookup_uuid")) {
|
||||
if (argc < 3) {
|
||||
printf("pv_lookup_uuid <uuid>\n");
|
||||
return -1;
|
||||
}
|
||||
uuid = argv[2];
|
||||
|
||||
reply = daemon_send_simple(h, "pv_lookup",
|
||||
"uuid = %s", uuid,
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else {
|
||||
printf("unknown command\n");
|
||||
goto out_close;
|
||||
}
|
||||
out:
|
||||
daemon_reply_destroy(reply);
|
||||
out_close:
|
||||
daemon_close(h);
|
||||
return 0;
|
||||
}
|
||||
@@ -14,23 +14,114 @@
|
||||
|
||||
#define _XOPEN_SOURCE 500 /* pthread */
|
||||
|
||||
#include "configure.h"
|
||||
#define _REENTRANT
|
||||
|
||||
#include "tool.h"
|
||||
|
||||
#include "daemon-io.h"
|
||||
#include "config-util.h"
|
||||
#include "daemon-server.h"
|
||||
#include "daemon-log.h"
|
||||
#include "lvm-version.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <math.h> /* fabs() */
|
||||
#include <float.h> /* DBL_EPSILON */
|
||||
|
||||
#define LVMETAD_SOCKET DEFAULT_RUN_DIR "/lvmetad.socket"
|
||||
|
||||
/*
|
||||
* valid/invalid state of cached metadata
|
||||
*
|
||||
* Normally when using lvmetad, the state is kept up-to-date through a
|
||||
* combination of notifications from clients and updates triggered by uevents.
|
||||
* When using lvmlockd, the lvmetad state is expected to become out of
|
||||
* date (invalid/stale) when other hosts make changes to the metadata on disk.
|
||||
*
|
||||
* To deal with this, the metadata cached in lvmetad can be flagged as invalid.
|
||||
* This invalid flag is returned along with the metadata when read by a
|
||||
* command. The command can check for the invalid flag and decide that it
|
||||
* should either use the stale metadata (uncommon), or read the latest metadata
|
||||
* from disk rather than using the invalid metadata that was returned. If the
|
||||
* command reads the latest metadata from disk, it can choose to send it to
|
||||
* lvmetad to update the cached copy and clear the invalid flag in lvmetad.
|
||||
* Otherwise, the next command to read the metadata from lvmetad will also
|
||||
* receive the invalid metadata with the invalid flag (and like the previous
|
||||
* command, it too may choose to read the latest metadata from disk and can
|
||||
* then also choose to update the lvmetad copy.)
|
||||
*
|
||||
* For purposes of tracking the invalid state, LVM metadata is considered
|
||||
* to be either VG-specific or global. VG-specific metadata is metadata
|
||||
* that is isolated to a VG, such as the LVs it contains. Global
|
||||
* metadata is metadata that is not isolated to a single VG. Global
|
||||
* metdata includes:
|
||||
* . the VG namespace (which VG names are used)
|
||||
* . the set of orphan PVs (which PVs are in VGs and which are not)
|
||||
* . properties of orphan PVs (the size of an orphan PV)
|
||||
*
|
||||
* If the metadata for a single VG becomes invalid, the VGFL_INVALID
|
||||
* flag can be set in the vg_info struct for that VG. If the global
|
||||
* metdata becomes invalid, the GLFL_INVALID flag can be set in the
|
||||
* lvmetad daemon state.
|
||||
*
|
||||
* If a command reads VG metadata and VGFL_INVALID is set, an
|
||||
* extra config node called "vg_invalid" is added to the config
|
||||
* data returned to the command.
|
||||
*
|
||||
* If a command reads global metdata and GLFL_INVALID is set, an
|
||||
* extra config node called "global_invalid" is added to the
|
||||
* config data returned to the command.
|
||||
*
|
||||
* If a command sees vg_invalid, and wants the latest VG metadata,
|
||||
* it only needs to scan disks of the PVs in that VG.
|
||||
* It can then use vg_update to send the latest metadata to lvmetad
|
||||
* which clears the VGFL_INVALID flag.
|
||||
*
|
||||
* If a command sees global_invalid, and wants the latest metadata,
|
||||
* it should scan all devices to update lvmetad, and then send
|
||||
* lvmetad the "set_global_info global_invalid=0" message to clear
|
||||
* GLFL_INVALID.
|
||||
*
|
||||
* (When rescanning devices to update lvmetad, the command must use
|
||||
* the global filter cmd->lvmetad_filter so that it processes the same
|
||||
* devices that are seen by lvmetad.)
|
||||
*
|
||||
* The lvmetad INVALID flags can be set by sending lvmetad the messages:
|
||||
*
|
||||
* . set_vg_info with the latest VG seqno. If the VG seqno is larger
|
||||
* than the cached VG seqno, VGFL_INVALID is set for the VG.
|
||||
*
|
||||
* . set_global_info with global_invalid=1 sets GLFL_INVALID.
|
||||
*
|
||||
* Different entities could use these functions to invalidate metadata
|
||||
* if/when they detected that the cache is stale. How they detect that
|
||||
* the cache is stale depends on the details of the specific entity.
|
||||
*
|
||||
* In the case of lvmlockd, it embeds values into its locks to keep track
|
||||
* of when other nodes have changed metadata on disk related to those locks.
|
||||
* When acquring locks it can look at these values and detect that
|
||||
* the metadata associated with the lock has been changed.
|
||||
* When the values change, it uses set_vg_info/set_global_info to
|
||||
* invalidate the lvmetad cache.
|
||||
*
|
||||
* The values that lvmlockd distributes through its locks are the
|
||||
* latest VG seqno in VG locks and a global counter in the global lock.
|
||||
* When a host acquires a VG lock and sees that the embedded seqno is
|
||||
* larger than it was previously, it knows that it should invalidate the
|
||||
* lvmetad cache for the VG. If the host acquires the global lock
|
||||
* and sees that the counter is larger than previously, it knows that
|
||||
* it should invalidate the global info in lvmetad. This invalidation
|
||||
* is done before the lock is returned to the command. This way the
|
||||
* invalid flag will be set on the metadata before the command reads
|
||||
* it from lvmetad.
|
||||
*/
|
||||
|
||||
struct vg_info {
|
||||
int64_t external_version;
|
||||
uint32_t flags; /* VGFL_ */
|
||||
};
|
||||
|
||||
#define GLFL_INVALID 0x00000001
|
||||
#define VGFL_INVALID 0x00000001
|
||||
|
||||
typedef struct {
|
||||
log_state *log; /* convenience */
|
||||
const char *log_config;
|
||||
@@ -40,6 +131,8 @@ typedef struct {
|
||||
|
||||
struct dm_hash_table *vgid_to_metadata;
|
||||
struct dm_hash_table *vgid_to_vgname;
|
||||
struct dm_hash_table *vgid_to_outdated_pvs;
|
||||
struct dm_hash_table *vgid_to_info;
|
||||
struct dm_hash_table *vgname_to_vgid;
|
||||
struct dm_hash_table *pvid_to_vgid;
|
||||
struct {
|
||||
@@ -50,6 +143,7 @@ typedef struct {
|
||||
pthread_mutex_t pvid_to_vgid;
|
||||
} lock;
|
||||
char token[128];
|
||||
uint32_t flags; /* GLFL_ */
|
||||
pthread_mutex_t token_lock;
|
||||
} lvmetad_state;
|
||||
|
||||
@@ -60,12 +154,17 @@ static void destroy_metadata_hashes(lvmetad_state *s)
|
||||
dm_hash_iterate(n, s->vgid_to_metadata)
|
||||
dm_config_destroy(dm_hash_get_data(s->vgid_to_metadata, n));
|
||||
|
||||
dm_hash_iterate(n, s->vgid_to_outdated_pvs)
|
||||
dm_config_destroy(dm_hash_get_data(s->vgid_to_outdated_pvs, n));
|
||||
|
||||
dm_hash_iterate(n, s->pvid_to_pvmeta)
|
||||
dm_config_destroy(dm_hash_get_data(s->pvid_to_pvmeta, n));
|
||||
|
||||
dm_hash_destroy(s->pvid_to_pvmeta);
|
||||
dm_hash_destroy(s->vgid_to_metadata);
|
||||
dm_hash_destroy(s->vgid_to_vgname);
|
||||
dm_hash_destroy(s->vgid_to_outdated_pvs);
|
||||
dm_hash_destroy(s->vgid_to_info);
|
||||
dm_hash_destroy(s->vgname_to_vgid);
|
||||
|
||||
dm_hash_destroy(s->device_to_pvid);
|
||||
@@ -78,6 +177,8 @@ static void create_metadata_hashes(lvmetad_state *s)
|
||||
s->device_to_pvid = dm_hash_create(32);
|
||||
s->vgid_to_metadata = dm_hash_create(32);
|
||||
s->vgid_to_vgname = dm_hash_create(32);
|
||||
s->vgid_to_outdated_pvs = dm_hash_create(32);
|
||||
s->vgid_to_info = dm_hash_create(32);
|
||||
s->pvid_to_vgid = dm_hash_create(32);
|
||||
s->vgname_to_vgid = dm_hash_create(32);
|
||||
}
|
||||
@@ -241,6 +342,30 @@ static int update_pv_status(lvmetad_state *s,
|
||||
return complete;
|
||||
}
|
||||
|
||||
static struct dm_config_node *add_last_node(struct dm_config_tree *cft, const char *node_name)
|
||||
{
|
||||
struct dm_config_node *cn, *last;
|
||||
|
||||
cn = cft->root;
|
||||
last = cn;
|
||||
|
||||
while (cn->sib) {
|
||||
last = cn->sib;
|
||||
cn = last;
|
||||
}
|
||||
|
||||
cn = dm_config_create_node(cft, node_name);
|
||||
if (!cn)
|
||||
return NULL;
|
||||
|
||||
cn->v = NULL;
|
||||
cn->sib = NULL;
|
||||
cn->parent = cft->root;
|
||||
last->sib = cn;
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
static struct dm_config_node *make_pv_node(lvmetad_state *s, const char *pvid,
|
||||
struct dm_config_tree *cft,
|
||||
struct dm_config_node *parent,
|
||||
@@ -304,6 +429,9 @@ static response pv_list(lvmetad_state *s, request r)
|
||||
cn = make_pv_node(s, id, res.cft, cn_pvs, cn);
|
||||
}
|
||||
|
||||
if (s->flags & GLFL_INVALID)
|
||||
add_last_node(res.cft, "global_invalid");
|
||||
|
||||
unlock_pvid_to_pvmeta(s);
|
||||
|
||||
return res;
|
||||
@@ -348,6 +476,9 @@ static response pv_lookup(lvmetad_state *s, request r)
|
||||
pv->key = "physical_volume";
|
||||
unlock_pvid_to_pvmeta(s);
|
||||
|
||||
if (s->flags & GLFL_INVALID)
|
||||
add_last_node(res.cft, "global_invalid");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -416,14 +547,87 @@ static response vg_list(lvmetad_state *s, request r)
|
||||
}
|
||||
|
||||
unlock_vgid_to_metadata(s);
|
||||
|
||||
if (s->flags & GLFL_INVALID)
|
||||
add_last_node(res.cft, "global_invalid");
|
||||
bad:
|
||||
return res;
|
||||
}
|
||||
|
||||
static void mark_outdated_pv(lvmetad_state *s, const char *vgid, const char *pvid)
|
||||
{
|
||||
struct dm_config_tree *pvmeta, *outdated_pvs;
|
||||
struct dm_config_node *list, *cft_vgid;
|
||||
struct dm_config_value *v;
|
||||
|
||||
lock_pvid_to_pvmeta(s);
|
||||
pvmeta = dm_hash_lookup(s->pvid_to_pvmeta, pvid);
|
||||
unlock_pvid_to_pvmeta(s);
|
||||
|
||||
/* if the MDA exists and is used, it will have ignore=0 set */
|
||||
if (!pvmeta ||
|
||||
(dm_config_find_int64(pvmeta->root, "pvmeta/mda0/ignore", 1) &&
|
||||
dm_config_find_int64(pvmeta->root, "pvmeta/mda1/ignore", 1)))
|
||||
return;
|
||||
|
||||
WARN(s, "PV %s has outdated metadata", pvid);
|
||||
|
||||
outdated_pvs = dm_hash_lookup(s->vgid_to_outdated_pvs, vgid);
|
||||
if (!outdated_pvs) {
|
||||
if (!(outdated_pvs = dm_config_from_string("outdated_pvs/pv_list = []")) ||
|
||||
!(cft_vgid = make_text_node(outdated_pvs, "vgid", dm_pool_strdup(outdated_pvs->mem, vgid),
|
||||
outdated_pvs->root, NULL)))
|
||||
abort();
|
||||
if(!dm_hash_insert(s->vgid_to_outdated_pvs, cft_vgid->v->v.str, outdated_pvs))
|
||||
abort();
|
||||
DEBUGLOG(s, "created outdated_pvs list for VG %s", vgid);
|
||||
}
|
||||
|
||||
list = dm_config_find_node(outdated_pvs->root, "outdated_pvs/pv_list");
|
||||
v = list->v;
|
||||
while (v) {
|
||||
if (v->type != DM_CFG_EMPTY_ARRAY && !strcmp(v->v.str, pvid))
|
||||
return;
|
||||
v = v->next;
|
||||
}
|
||||
if (!(v = dm_config_create_value(outdated_pvs)))
|
||||
abort();
|
||||
v->type = DM_CFG_STRING;
|
||||
v->v.str = dm_pool_strdup(outdated_pvs->mem, pvid);
|
||||
v->next = list->v;
|
||||
list->v = v;
|
||||
}
|
||||
|
||||
static void chain_outdated_pvs(lvmetad_state *s, const char *vgid, struct dm_config_tree *metadata_cft, struct dm_config_node *metadata)
|
||||
{
|
||||
struct dm_config_tree *cft = dm_hash_lookup(s->vgid_to_outdated_pvs, vgid), *pvmeta;
|
||||
struct dm_config_node *pv, *res, *out_pvs = cft ? dm_config_find_node(cft->root, "outdated_pvs/pv_list") : NULL;
|
||||
struct dm_config_value *pvs_v = out_pvs ? out_pvs->v : NULL;
|
||||
if (!pvs_v)
|
||||
return;
|
||||
if (!(res = make_config_node(metadata_cft, "outdated_pvs", metadata_cft->root, 0)))
|
||||
return; /* oops */
|
||||
res->sib = metadata->child;
|
||||
metadata->child = res;
|
||||
for (; pvs_v && pvs_v->type != DM_CFG_EMPTY_ARRAY; pvs_v = pvs_v->next) {
|
||||
pvmeta = dm_hash_lookup(s->pvid_to_pvmeta, pvs_v->v.str);
|
||||
if (!pvmeta) {
|
||||
WARN(s, "metadata for PV %s not found", pvs_v->v.str);
|
||||
continue;
|
||||
}
|
||||
if (!(pv = dm_config_clone_node(metadata_cft, pvmeta->root, 0)))
|
||||
continue;
|
||||
pv->key = dm_config_find_str(pv, "pvmeta/id", NULL);
|
||||
pv->sib = res->child;
|
||||
res->child = pv;
|
||||
}
|
||||
}
|
||||
|
||||
static response vg_lookup(lvmetad_state *s, request r)
|
||||
{
|
||||
struct dm_config_tree *cft;
|
||||
struct dm_config_node *metadata, *n;
|
||||
struct vg_info *info;
|
||||
response res = { 0 };
|
||||
|
||||
const char *uuid = daemon_request_str(r, "uuid", NULL);
|
||||
@@ -486,6 +690,17 @@ static response vg_lookup(lvmetad_state *s, request r)
|
||||
unlock_vg(s, uuid);
|
||||
|
||||
update_pv_status(s, res.cft, n, 1); /* FIXME report errors */
|
||||
chain_outdated_pvs(s, uuid, res.cft, n);
|
||||
|
||||
if (s->flags & GLFL_INVALID)
|
||||
add_last_node(res.cft, "global_invalid");
|
||||
|
||||
info = dm_hash_lookup(s->vgid_to_info, uuid);
|
||||
if (info && (info->flags & VGFL_INVALID)) {
|
||||
n = add_last_node(res.cft, "vg_invalid");
|
||||
if (!n)
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return res;
|
||||
bad:
|
||||
@@ -493,65 +708,13 @@ bad:
|
||||
return reply_fail("out of memory");
|
||||
}
|
||||
|
||||
/* Test if the doubles are close enough to be considered equal */
|
||||
static int close_enough(double d1, double d2)
|
||||
{
|
||||
return fabs(d1 - d2) < DBL_EPSILON;
|
||||
}
|
||||
|
||||
static int compare_value(struct dm_config_value *a, struct dm_config_value *b)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if (a->type > b->type)
|
||||
return 1;
|
||||
if (a->type < b->type)
|
||||
return -1;
|
||||
|
||||
switch (a->type) {
|
||||
case DM_CFG_STRING: r = strcmp(a->v.str, b->v.str); break;
|
||||
case DM_CFG_FLOAT: r = close_enough(a->v.f, b->v.f) ? 0 : (a->v.f > b->v.f) ? 1 : -1; break;
|
||||
case DM_CFG_INT: r = (a->v.i == b->v.i) ? 0 : (a->v.i > b->v.i) ? 1 : -1; break;
|
||||
case DM_CFG_EMPTY_ARRAY: return 0;
|
||||
}
|
||||
|
||||
if (r == 0 && a->next && b->next)
|
||||
r = compare_value(a->next, b->next);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int compare_config(struct dm_config_node *a, struct dm_config_node *b)
|
||||
{
|
||||
int result = 0;
|
||||
if (a->v && b->v)
|
||||
result = compare_value(a->v, b->v);
|
||||
if (a->v && !b->v)
|
||||
result = 1;
|
||||
if (!a->v && b->v)
|
||||
result = -1;
|
||||
if (a->child && b->child)
|
||||
result = compare_config(a->child, b->child);
|
||||
|
||||
if (result) {
|
||||
// DEBUGLOG("config inequality at %s / %s", a->key, b->key);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (a->sib && b->sib)
|
||||
result = compare_config(a->sib, b->sib);
|
||||
if (a->sib && !b->sib)
|
||||
result = 1;
|
||||
if (!a->sib && b->sib)
|
||||
result = -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int vg_remove_if_missing(lvmetad_state *s, const char *vgid, int update_pvids);
|
||||
|
||||
enum update_pvid_mode { UPDATE_ONLY, REMOVE_EMPTY, MARK_OUTDATED };
|
||||
|
||||
/* You need to be holding the pvid_to_vgid lock already to call this. */
|
||||
static int update_pvid_to_vgid(lvmetad_state *s, struct dm_config_tree *vg,
|
||||
const char *vgid, int nuke_empty)
|
||||
const char *vgid, int mode)
|
||||
{
|
||||
struct dm_config_node *pv;
|
||||
struct dm_hash_table *to_check;
|
||||
@@ -571,11 +734,14 @@ static int update_pvid_to_vgid(lvmetad_state *s, struct dm_config_tree *vg,
|
||||
if (!(pvid = dm_config_find_str(pv->child, "id", NULL)))
|
||||
continue;
|
||||
|
||||
if (nuke_empty &&
|
||||
if (mode == REMOVE_EMPTY &&
|
||||
(vgid_old = dm_hash_lookup(s->pvid_to_vgid, pvid)) &&
|
||||
!dm_hash_insert(to_check, vgid_old, (void*) 1))
|
||||
goto out;
|
||||
|
||||
if (mode == MARK_OUTDATED)
|
||||
mark_outdated_pv(s, vgid, pvid);
|
||||
|
||||
if (!dm_hash_insert(s->pvid_to_vgid, pvid, (void*) vgid))
|
||||
goto out;
|
||||
|
||||
@@ -599,10 +765,11 @@ static int update_pvid_to_vgid(lvmetad_state *s, struct dm_config_tree *vg,
|
||||
/* A pvid map lock needs to be held if update_pvids = 1. */
|
||||
static int remove_metadata(lvmetad_state *s, const char *vgid, int update_pvids)
|
||||
{
|
||||
struct dm_config_tree *old;
|
||||
struct dm_config_tree *old, *outdated_pvs;
|
||||
const char *oldname;
|
||||
lock_vgid_to_metadata(s);
|
||||
old = dm_hash_lookup(s->vgid_to_metadata, vgid);
|
||||
outdated_pvs = dm_hash_lookup(s->vgid_to_outdated_pvs, vgid);
|
||||
oldname = dm_hash_lookup(s->vgid_to_vgname, vgid);
|
||||
|
||||
if (!old) {
|
||||
@@ -616,12 +783,15 @@ static int remove_metadata(lvmetad_state *s, const char *vgid, int update_pvids)
|
||||
dm_hash_remove(s->vgid_to_metadata, vgid);
|
||||
dm_hash_remove(s->vgid_to_vgname, vgid);
|
||||
dm_hash_remove(s->vgname_to_vgid, oldname);
|
||||
dm_hash_remove(s->vgid_to_outdated_pvs, vgid);
|
||||
unlock_vgid_to_metadata(s);
|
||||
|
||||
if (update_pvids)
|
||||
/* FIXME: What should happen when update fails */
|
||||
update_pvid_to_vgid(s, old, "#orphan", 0);
|
||||
dm_config_destroy(old);
|
||||
if (outdated_pvs)
|
||||
dm_config_destroy(outdated_pvs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -665,7 +835,7 @@ static int vg_remove_if_missing(lvmetad_state *s, const char *vgid, int update_p
|
||||
* this function, so they can be safely destroyed after update_metadata returns
|
||||
* (anything that might have been retained is copied). */
|
||||
static int update_metadata(lvmetad_state *s, const char *name, const char *_vgid,
|
||||
struct dm_config_node *metadata, int64_t *oldseq)
|
||||
struct dm_config_node *metadata, int64_t *oldseq, const char *pvid)
|
||||
{
|
||||
struct dm_config_tree *cft = NULL;
|
||||
struct dm_config_tree *old;
|
||||
@@ -714,6 +884,10 @@ static int update_metadata(lvmetad_state *s, const char *name, const char *_vgid
|
||||
|
||||
if (seq < haveseq) {
|
||||
DEBUGLOG(s, "Refusing to update metadata for %s (at %d) to %d", _vgid, haveseq, seq);
|
||||
|
||||
if (pvid)
|
||||
mark_outdated_pv(s, dm_config_find_str(old->root, "metadata/id", NULL), pvid);
|
||||
|
||||
/* TODO: notify the client that their metadata is out of date? */
|
||||
retval = 1;
|
||||
goto out;
|
||||
@@ -736,6 +910,8 @@ static int update_metadata(lvmetad_state *s, const char *name, const char *_vgid
|
||||
|
||||
if (haveseq >= 0 && haveseq < seq) {
|
||||
INFO(s, "Updating metadata for %s at %d to %d", _vgid, haveseq, seq);
|
||||
if (oldseq)
|
||||
update_pvid_to_vgid(s, old, vgid, MARK_OUTDATED);
|
||||
/* temporarily orphan all of our PVs */
|
||||
update_pvid_to_vgid(s, old, "#orphan", 0);
|
||||
}
|
||||
@@ -809,8 +985,7 @@ static response pv_gone(lvmetad_state *s, request r)
|
||||
int64_t device = daemon_request_int(r, "device", 0);
|
||||
int64_t alt_device = 0;
|
||||
struct dm_config_tree *pvmeta;
|
||||
struct dm_config_node *pvmeta_tmp;
|
||||
char *pvid_old, *vgid;
|
||||
char *vgid;
|
||||
|
||||
DEBUGLOG(s, "pv_gone: %s / %" PRIu64, pvid, device);
|
||||
|
||||
@@ -824,8 +999,8 @@ static response pv_gone(lvmetad_state *s, request r)
|
||||
|
||||
DEBUGLOG(s, "pv_gone (updated): %s / %" PRIu64, pvid, device);
|
||||
|
||||
pvmeta = dm_hash_lookup(s->pvid_to_pvmeta, pvid);
|
||||
pvid_old = dm_hash_lookup_binary(s->device_to_pvid, &device, sizeof(device));
|
||||
if (!(pvmeta = dm_hash_lookup(s->pvid_to_pvmeta, pvid)))
|
||||
return reply_unknown("PVID does not exist");
|
||||
vgid = dm_hash_lookup(s->pvid_to_vgid, pvid);
|
||||
|
||||
dm_hash_remove_binary(s->device_to_pvid, &device, sizeof(device));
|
||||
@@ -847,9 +1022,6 @@ static response pv_gone(lvmetad_state *s, request r)
|
||||
dm_free(vgid);
|
||||
}
|
||||
|
||||
if (!pvmeta)
|
||||
return reply_unknown("PVID does not exist");
|
||||
|
||||
if (!alt_device)
|
||||
dm_config_destroy(pvmeta);
|
||||
|
||||
@@ -952,8 +1124,8 @@ static response pv_found(lvmetad_state *s, request r)
|
||||
altdev->v->v.i = device_old_pvid;
|
||||
break;
|
||||
}
|
||||
};
|
||||
altdev_v = altdev->v;
|
||||
};
|
||||
altdev_v = altdev->v;
|
||||
while (altdev_v) {
|
||||
if (altdev_v->next && altdev_v->next->v.i == device)
|
||||
altdev_v->next = altdev_v->next->next;
|
||||
@@ -994,7 +1166,7 @@ out_of_mem:
|
||||
if (daemon_request_int(r, "metadata/seqno", -1) < 0)
|
||||
return reply_fail("need VG seqno");
|
||||
|
||||
if (!update_metadata(s, vgname, vgid, metadata, &seqno_old))
|
||||
if (!update_metadata(s, vgname, vgid, metadata, &seqno_old, pvid))
|
||||
return reply_fail("metadata update failed");
|
||||
changed |= (seqno_old != dm_config_find_int(metadata, "metadata/seqno", -1));
|
||||
} else {
|
||||
@@ -1042,6 +1214,39 @@ out_of_mem:
|
||||
NULL);
|
||||
}
|
||||
|
||||
static response vg_clear_outdated_pvs(lvmetad_state *s, request r)
|
||||
{
|
||||
struct dm_config_tree *outdated_pvs;
|
||||
const char *vgid = daemon_request_str(r, "vgid", NULL);
|
||||
|
||||
if (!vgid)
|
||||
return reply_fail("need VG UUID");
|
||||
|
||||
if ((outdated_pvs = dm_hash_lookup(s->vgid_to_outdated_pvs, vgid))) {
|
||||
dm_config_destroy(outdated_pvs);
|
||||
dm_hash_remove(s->vgid_to_outdated_pvs, vgid);
|
||||
}
|
||||
return daemon_reply_simple("OK", NULL);
|
||||
}
|
||||
|
||||
static void vg_info_update(lvmetad_state *s, const char *uuid,
|
||||
struct dm_config_node *metadata)
|
||||
{
|
||||
struct vg_info *info;
|
||||
int64_t cache_version;
|
||||
|
||||
cache_version = dm_config_find_int64(metadata, "metadata/seqno", -1);
|
||||
if (cache_version == -1)
|
||||
return;
|
||||
|
||||
info = (struct vg_info *) dm_hash_lookup(s->vgid_to_info, uuid);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
if (cache_version >= info->external_version)
|
||||
info->flags &= ~VGFL_INVALID;
|
||||
}
|
||||
|
||||
static response vg_update(lvmetad_state *s, request r)
|
||||
{
|
||||
struct dm_config_node *metadata = dm_config_find_node(r.cft->root, "metadata");
|
||||
@@ -1057,8 +1262,10 @@ static response vg_update(lvmetad_state *s, request r)
|
||||
|
||||
/* TODO defer metadata update here; add a separate vg_commit
|
||||
* call; if client does not commit, die */
|
||||
if (!update_metadata(s, vgname, vgid, metadata, NULL))
|
||||
if (!update_metadata(s, vgname, vgid, metadata, NULL, NULL))
|
||||
return reply_fail("metadata update failed");
|
||||
|
||||
vg_info_update(s, vgid, metadata);
|
||||
}
|
||||
return daemon_reply_simple("OK", NULL);
|
||||
}
|
||||
@@ -1079,6 +1286,71 @@ static response vg_remove(lvmetad_state *s, request r)
|
||||
return daemon_reply_simple("OK", NULL);
|
||||
}
|
||||
|
||||
static response set_global_info(lvmetad_state *s, request r)
|
||||
{
|
||||
const int global_invalid = daemon_request_int(r, "global_invalid", -1);
|
||||
|
||||
if (global_invalid == 1)
|
||||
s->flags |= GLFL_INVALID;
|
||||
|
||||
else if (global_invalid == 0)
|
||||
s->flags &= ~GLFL_INVALID;
|
||||
|
||||
return daemon_reply_simple("OK", NULL);
|
||||
}
|
||||
|
||||
static response get_global_info(lvmetad_state *s, request r)
|
||||
{
|
||||
return daemon_reply_simple("OK", "global_invalid = %d",
|
||||
(s->flags & GLFL_INVALID) ? 1 : 0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static response set_vg_info(lvmetad_state *s, request r)
|
||||
{
|
||||
struct dm_config_tree *vg;
|
||||
struct vg_info *info;
|
||||
const char *uuid = daemon_request_str(r, "uuid", NULL);
|
||||
const int64_t new_version = daemon_request_int(r, "version", -1);
|
||||
int64_t cache_version;
|
||||
|
||||
if (!uuid)
|
||||
goto out;
|
||||
|
||||
if (new_version == -1)
|
||||
goto out;
|
||||
|
||||
vg = dm_hash_lookup(s->vgid_to_metadata, uuid);
|
||||
if (!vg)
|
||||
goto out;
|
||||
|
||||
if (!new_version)
|
||||
goto inval;
|
||||
|
||||
cache_version = dm_config_find_int64(vg->root, "metadata/seqno", -1);
|
||||
|
||||
if (cache_version != -1 && new_version != -1 && cache_version >= new_version)
|
||||
goto out;
|
||||
inval:
|
||||
info = dm_hash_lookup(s->vgid_to_info, uuid);
|
||||
if (!info) {
|
||||
info = malloc(sizeof(struct vg_info));
|
||||
if (!info)
|
||||
goto bad;
|
||||
memset(info, 0, sizeof(struct vg_info));
|
||||
if (!dm_hash_insert(s->vgid_to_info, uuid, (void*)info))
|
||||
goto bad;
|
||||
}
|
||||
|
||||
info->external_version = new_version;
|
||||
info->flags |= VGFL_INVALID;
|
||||
|
||||
out:
|
||||
return daemon_reply_simple("OK", NULL);
|
||||
bad:
|
||||
return reply_fail("out of memory");
|
||||
}
|
||||
|
||||
static void _dump_cft(struct buffer *buf, struct dm_hash_table *ht, const char *key_addr)
|
||||
{
|
||||
struct dm_hash_node *n;
|
||||
@@ -1116,6 +1388,52 @@ static void _dump_pairs(struct buffer *buf, struct dm_hash_table *ht, const char
|
||||
buffer_append(buf, "}\n");
|
||||
}
|
||||
|
||||
static void _dump_info_version(struct buffer *buf, struct dm_hash_table *ht, const char *name, int int_key)
|
||||
{
|
||||
char *append;
|
||||
struct dm_hash_node *n = dm_hash_get_first(ht);
|
||||
struct vg_info *info;
|
||||
|
||||
buffer_append(buf, name);
|
||||
buffer_append(buf, " {\n");
|
||||
|
||||
while (n) {
|
||||
const char *key = dm_hash_get_key(ht, n);
|
||||
info = dm_hash_get_data(ht, n);
|
||||
buffer_append(buf, " ");
|
||||
(void) dm_asprintf(&append, "%s = %lld", key, (long long)info->external_version);
|
||||
if (append)
|
||||
buffer_append(buf, append);
|
||||
buffer_append(buf, "\n");
|
||||
dm_free(append);
|
||||
n = dm_hash_get_next(ht, n);
|
||||
}
|
||||
buffer_append(buf, "}\n");
|
||||
}
|
||||
|
||||
static void _dump_info_flags(struct buffer *buf, struct dm_hash_table *ht, const char *name, int int_key)
|
||||
{
|
||||
char *append;
|
||||
struct dm_hash_node *n = dm_hash_get_first(ht);
|
||||
struct vg_info *info;
|
||||
|
||||
buffer_append(buf, name);
|
||||
buffer_append(buf, " {\n");
|
||||
|
||||
while (n) {
|
||||
const char *key = dm_hash_get_key(ht, n);
|
||||
info = dm_hash_get_data(ht, n);
|
||||
buffer_append(buf, " ");
|
||||
(void) dm_asprintf(&append, "%s = %llx", key, (long long)info->flags);
|
||||
if (append)
|
||||
buffer_append(buf, append);
|
||||
buffer_append(buf, "\n");
|
||||
dm_free(append);
|
||||
n = dm_hash_get_next(ht, n);
|
||||
}
|
||||
buffer_append(buf, "}\n");
|
||||
}
|
||||
|
||||
static response dump(lvmetad_state *s)
|
||||
{
|
||||
response res = { 0 };
|
||||
@@ -1138,6 +1456,9 @@ static response dump(lvmetad_state *s)
|
||||
buffer_append(b, "\n# VGID to VGNAME mapping\n\n");
|
||||
_dump_pairs(b, s->vgid_to_vgname, "vgid_to_vgname", 0);
|
||||
|
||||
buffer_append(b, "\n# VGID to outdated PVs mapping\n\n");
|
||||
_dump_cft(b, s->vgid_to_outdated_pvs, "outdated_pvs/vgid");
|
||||
|
||||
buffer_append(b, "\n# VGNAME to VGID mapping\n\n");
|
||||
_dump_pairs(b, s->vgname_to_vgid, "vgname_to_vgid", 0);
|
||||
|
||||
@@ -1147,6 +1468,12 @@ static response dump(lvmetad_state *s)
|
||||
buffer_append(b, "\n# DEVICE to PVID mapping\n\n");
|
||||
_dump_pairs(b, s->device_to_pvid, "device_to_pvid", 1);
|
||||
|
||||
buffer_append(b, "\n# VGID to INFO version mapping\n\n");
|
||||
_dump_info_version(b, s->vgid_to_info, "vgid_to_info", 0);
|
||||
|
||||
buffer_append(b, "\n# VGID to INFO flags mapping\n\n");
|
||||
_dump_info_flags(b, s->vgid_to_info, "vgid_to_info", 0);
|
||||
|
||||
unlock_pvid_to_vgid(s);
|
||||
unlock_pvid_to_pvmeta(s);
|
||||
unlock_vgid_to_metadata(s);
|
||||
@@ -1168,7 +1495,7 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
return daemon_reply_simple("OK", NULL);
|
||||
}
|
||||
|
||||
if (strcmp(token, state->token) && strcmp(rq, "dump")) {
|
||||
if (strcmp(token, state->token) && strcmp(rq, "dump") && strcmp(token, "skip")) {
|
||||
pthread_mutex_unlock(&state->token_lock);
|
||||
return daemon_reply_simple("token_mismatch",
|
||||
"expected = %s", state->token,
|
||||
@@ -1197,6 +1524,9 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
if (!strcmp(rq, "vg_update"))
|
||||
return vg_update(state, r);
|
||||
|
||||
if (!strcmp(rq, "vg_clear_outdated_pvs"))
|
||||
return vg_clear_outdated_pvs(state, r);
|
||||
|
||||
if (!strcmp(rq, "vg_remove"))
|
||||
return vg_remove(state, r);
|
||||
|
||||
@@ -1209,6 +1539,15 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
if (!strcmp(rq, "vg_list"))
|
||||
return vg_list(state, r);
|
||||
|
||||
if (!strcmp(rq, "set_global_info"))
|
||||
return set_global_info(state, r);
|
||||
|
||||
if (!strcmp(rq, "get_global_info"))
|
||||
return get_global_info(state, r);
|
||||
|
||||
if (!strcmp(rq, "set_vg_info"))
|
||||
return set_vg_info(state, r);
|
||||
|
||||
if (!strcmp(rq, "dump"))
|
||||
return dump(state);
|
||||
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 Red Hat, Inc.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "tool.h"
|
||||
|
||||
#include "lvmetad-client.h"
|
||||
#include "label.h"
|
||||
#include "lvmcache.h"
|
||||
@@ -109,7 +124,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (argc > 1) {
|
||||
int i;
|
||||
struct cmd_context *cmd = create_toolcontext(0, NULL, 0, 0);
|
||||
struct cmd_context *cmd = create_toolcontext(0, NULL, 0, 0, 1, 1);
|
||||
for (i = 1; i < argc; ++i) {
|
||||
const char *uuid = NULL;
|
||||
scan(h, argv[i]);
|
||||
|
||||
2
daemons/lvmlockd/.gitignore
vendored
Normal file
2
daemons/lvmlockd/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
lvmlockctl
|
||||
lvmlockd
|
||||
66
daemons/lvmlockd/Makefile.in
Normal file
66
daemons/lvmlockd/Makefile.in
Normal file
@@ -0,0 +1,66 @@
|
||||
#
|
||||
# Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
#
|
||||
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SOURCES = lvmlockd-core.c
|
||||
|
||||
ifeq ("@BUILD_LOCKDSANLOCK@", "yes")
|
||||
SOURCES += lvmlockd-sanlock.c
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LOCKDDLM@", "yes")
|
||||
SOURCES += lvmlockd-dlm.c
|
||||
endif
|
||||
|
||||
TARGETS = lvmlockd lvmlockctl
|
||||
|
||||
.PHONY: install_lvmlockd
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
INCLUDES += -I$(top_srcdir)/libdaemon/server
|
||||
LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper
|
||||
|
||||
LIBS += $(PTHREAD_LIBS)
|
||||
|
||||
ifeq ("@BUILD_LOCKDSANLOCK@", "yes")
|
||||
LIBS += -lsanlock_client
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LOCKDDLM@", "yes")
|
||||
LIBS += -ldlm_lt
|
||||
endif
|
||||
|
||||
LDFLAGS += -L$(top_builddir)/libdaemon/server
|
||||
CLDFLAGS += -L$(top_builddir)/libdaemon/server
|
||||
|
||||
lvmlockd: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS)
|
||||
|
||||
lvmlockctl: lvmlockctl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmlockctl.o $(LVMLIBS)
|
||||
|
||||
install_lvmlockd: lvmlockd
|
||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
||||
|
||||
install_lvmlockctl: lvmlockctl
|
||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
||||
|
||||
install_lvm2: install_lvmlockd install_lvmlockctl
|
||||
|
||||
install: install_lvm2
|
||||
745
daemons/lvmlockd/lvmlockctl.c
Normal file
745
daemons/lvmlockd/lvmlockctl.c
Normal file
@@ -0,0 +1,745 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "tool.h"
|
||||
|
||||
#include "lvmlockd-client.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
static int quit = 0;
|
||||
static int info = 0;
|
||||
static int dump = 0;
|
||||
static int wait_opt = 0;
|
||||
static int force_opt = 0;
|
||||
static int kill_vg = 0;
|
||||
static int drop_vg = 0;
|
||||
static int gl_enable = 0;
|
||||
static int gl_disable = 0;
|
||||
static int stop_lockspaces = 0;
|
||||
static char *arg_vg_name = NULL;
|
||||
|
||||
#define DUMP_SOCKET_NAME "lvmlockd-dump.sock"
|
||||
#define DUMP_BUF_SIZE (1024 * 1024)
|
||||
static char dump_buf[DUMP_BUF_SIZE+1];
|
||||
static int dump_len;
|
||||
static struct sockaddr_un dump_addr;
|
||||
static socklen_t dump_addrlen;
|
||||
|
||||
daemon_handle _lvmlockd;
|
||||
|
||||
#define log_error(fmt, args...) \
|
||||
do { \
|
||||
printf(fmt "\n", ##args); \
|
||||
} while (0)
|
||||
|
||||
#define MAX_LINE 512
|
||||
|
||||
/* copied from lvmlockd-internal.h */
|
||||
#define MAX_NAME 64
|
||||
#define MAX_ARGS 64
|
||||
|
||||
/*
|
||||
* lvmlockd dumps the client info before the lockspaces,
|
||||
* so we can look up client info when printing lockspace info.
|
||||
*/
|
||||
|
||||
#define MAX_CLIENTS 100
|
||||
|
||||
struct client_info {
|
||||
uint32_t client_id;
|
||||
int pid;
|
||||
char name[MAX_NAME+1];
|
||||
};
|
||||
|
||||
static struct client_info clients[MAX_CLIENTS];
|
||||
static int num_clients;
|
||||
|
||||
static void save_client_info(char *line)
|
||||
{
|
||||
uint32_t pid = 0;
|
||||
int fd = 0;
|
||||
int pi = 0;
|
||||
uint32_t client_id = 0;
|
||||
char name[MAX_NAME+1] = { 0 };
|
||||
|
||||
sscanf(line, "info=client pid=%u fd=%d pi=%d id=%u name=%s",
|
||||
&pid, &fd, &pi, &client_id, name);
|
||||
|
||||
clients[num_clients].client_id = client_id;
|
||||
clients[num_clients].pid = pid;
|
||||
strcpy(clients[num_clients].name, name);
|
||||
num_clients++;
|
||||
}
|
||||
|
||||
static void find_client_info(uint32_t client_id, uint32_t *pid, char *cl_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_clients; i++) {
|
||||
if (clients[i].client_id == client_id) {
|
||||
*pid = clients[i].pid;
|
||||
strcpy(cl_name, clients[i].name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int first_ls = 1;
|
||||
|
||||
static void format_info_ls(char *line)
|
||||
{
|
||||
char ls_name[MAX_NAME+1] = { 0 };
|
||||
char vg_name[MAX_NAME+1] = { 0 };
|
||||
char vg_uuid[MAX_NAME+1] = { 0 };
|
||||
char vg_sysid[MAX_NAME+1] = { 0 };
|
||||
char lock_args[MAX_ARGS+1] = { 0 };
|
||||
char lock_type[MAX_NAME+1] = { 0 };
|
||||
|
||||
sscanf(line, "info=ls ls_name=%s vg_name=%s vg_uuid=%s vg_sysid=%s vg_args=%s lm_type=%s",
|
||||
ls_name, vg_name, vg_uuid, vg_sysid, lock_args, lock_type);
|
||||
|
||||
if (!first_ls)
|
||||
printf("\n");
|
||||
first_ls = 0;
|
||||
|
||||
printf("VG %s lock_type=%s %s\n", vg_name, lock_type, vg_uuid);
|
||||
|
||||
printf("LS %s %s\n", lock_type, ls_name);
|
||||
}
|
||||
|
||||
static void format_info_ls_action(char *line)
|
||||
{
|
||||
uint32_t client_id = 0;
|
||||
char flags[MAX_NAME+1] = { 0 };
|
||||
char version[MAX_NAME+1] = { 0 };
|
||||
char op[MAX_NAME+1] = { 0 };
|
||||
uint32_t pid = 0;
|
||||
char cl_name[MAX_NAME+1] = { 0 };
|
||||
|
||||
sscanf(line, "info=ls_action client_id=%u %s %s op=%s",
|
||||
&client_id, flags, version, op);
|
||||
|
||||
find_client_info(client_id, &pid, cl_name);
|
||||
|
||||
printf("OP %s pid %u (%s)\n", op, pid, cl_name);
|
||||
}
|
||||
|
||||
static void format_info_r(char *line, char *r_name_out, char *r_type_out)
|
||||
{
|
||||
char r_name[MAX_NAME+1] = { 0 };
|
||||
char r_type[4] = { 0 };
|
||||
char mode[4] = { 0 };
|
||||
char sh_count[MAX_NAME+1] = { 0 };
|
||||
uint32_t ver = 0;
|
||||
|
||||
sscanf(line, "info=r name=%s type=%s mode=%s %s version=%u",
|
||||
r_name, r_type, mode, sh_count, &ver);
|
||||
|
||||
/* when mode is not un, wait and print each lk line */
|
||||
|
||||
if (strcmp(mode, "un")) {
|
||||
strcpy(r_name_out, r_name);
|
||||
strcpy(r_type_out, r_type);
|
||||
return;
|
||||
}
|
||||
|
||||
/* when mode is un, there will be no lk lines, so print now */
|
||||
|
||||
if (!strcmp(r_type, "gl")) {
|
||||
printf("LK GL un ver %u\n", ver);
|
||||
|
||||
} else if (!strcmp(r_type, "vg")) {
|
||||
printf("LK VG un ver %u\n", ver);
|
||||
|
||||
} else if (!strcmp(r_type, "lv")) {
|
||||
printf("LK LV un %s\n", r_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void format_info_lk(char *line, char *r_name, char *r_type)
|
||||
{
|
||||
char mode[4] = { 0 };
|
||||
uint32_t ver = 0;
|
||||
char flags[MAX_NAME+1] = { 0 };
|
||||
uint32_t client_id = 0;
|
||||
uint32_t pid = 0;
|
||||
char cl_name[MAX_NAME+1] = { 0 };
|
||||
|
||||
if (!r_name[0] || !r_type[0]) {
|
||||
printf("format_info_lk error r_name %s r_type %s\n", r_name, r_type);
|
||||
printf("%s\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
sscanf(line, "info=lk mode=%s version=%u %s client_id=%u",
|
||||
mode, &ver, flags, &client_id);
|
||||
|
||||
find_client_info(client_id, &pid, cl_name);
|
||||
|
||||
if (!strcmp(r_type, "gl")) {
|
||||
printf("LK GL %s ver %u pid %u (%s)\n", mode, ver, pid, cl_name);
|
||||
|
||||
} else if (!strcmp(r_type, "vg")) {
|
||||
printf("LK VG %s ver %u pid %u (%s)\n", mode, ver, pid, cl_name);
|
||||
|
||||
} else if (!strcmp(r_type, "lv")) {
|
||||
printf("LK LV %s %s\n", mode, r_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void format_info_r_action(char *line, char *r_name, char *r_type)
|
||||
{
|
||||
uint32_t client_id = 0;
|
||||
char flags[MAX_NAME+1] = { 0 };
|
||||
char version[MAX_NAME+1] = { 0 };
|
||||
char op[MAX_NAME+1] = { 0 };
|
||||
char rt[4] = { 0 };
|
||||
char mode[4] = { 0 };
|
||||
char lm[MAX_NAME+1] = { 0 };
|
||||
char result[MAX_NAME+1] = { 0 };
|
||||
char lm_rv[MAX_NAME+1] = { 0 };
|
||||
uint32_t pid = 0;
|
||||
char cl_name[MAX_NAME+1] = { 0 };
|
||||
|
||||
if (!r_name[0] || !r_type[0]) {
|
||||
printf("format_info_r_action error r_name %s r_type %s\n", r_name, r_type);
|
||||
printf("%s\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
sscanf(line, "info=r_action client_id=%u %s %s op=%s rt=%s mode=%s %s %s %s",
|
||||
&client_id, flags, version, op, rt, mode, lm, result, lm_rv);
|
||||
|
||||
find_client_info(client_id, &pid, cl_name);
|
||||
|
||||
if (strcmp(op, "lock")) {
|
||||
printf("OP %s pid %u (%s)", op, pid, cl_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(r_type, "gl")) {
|
||||
printf("LW GL %s ver %u pid %u (%s)\n", mode, 0, pid, cl_name);
|
||||
|
||||
} else if (!strcmp(r_type, "vg")) {
|
||||
printf("LW VG %s ver %u pid %u (%s)\n", mode, 0, pid, cl_name);
|
||||
|
||||
} else if (!strcmp(r_type, "lv")) {
|
||||
printf("LW LV %s %s\n", mode, r_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void format_info_line(char *line, char *r_name, char *r_type)
|
||||
{
|
||||
if (!strncmp(line, "info=structs ", strlen("info=structs "))) {
|
||||
/* only print this in the raw info dump */
|
||||
|
||||
} else if (!strncmp(line, "info=client ", strlen("info=client "))) {
|
||||
save_client_info(line);
|
||||
|
||||
} else if (!strncmp(line, "info=ls ", strlen("info=ls "))) {
|
||||
format_info_ls(line);
|
||||
|
||||
} else if (!strncmp(line, "info=ls_action ", strlen("info=ls_action "))) {
|
||||
format_info_ls_action(line);
|
||||
|
||||
} else if (!strncmp(line, "info=r ", strlen("info=r "))) {
|
||||
/*
|
||||
* r_name/r_type are reset when a new resource is found.
|
||||
* They are reused for the lock and action lines that
|
||||
* follow a resource line.
|
||||
*/
|
||||
memset(r_name, 0, MAX_NAME+1);
|
||||
memset(r_type, 0, MAX_NAME+1);
|
||||
format_info_r(line, r_name, r_type);
|
||||
|
||||
} else if (!strncmp(line, "info=lk ", strlen("info=lk "))) {
|
||||
/* will use info from previous r */
|
||||
format_info_lk(line, r_name, r_type);
|
||||
|
||||
} else if (!strncmp(line, "info=r_action ", strlen("info=r_action "))) {
|
||||
/* will use info from previous r */
|
||||
format_info_r_action(line, r_name, r_type);
|
||||
} else {
|
||||
printf("UN %s\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
static void format_info(void)
|
||||
{
|
||||
char line[MAX_LINE];
|
||||
char r_name[MAX_NAME+1];
|
||||
char r_type[MAX_NAME+1];
|
||||
int i, j;
|
||||
|
||||
j = 0;
|
||||
memset(line, 0, sizeof(line));
|
||||
|
||||
for (i = 0; i < dump_len; i++) {
|
||||
line[j++] = dump_buf[i];
|
||||
|
||||
if ((line[j-1] == '\n') || (line[j-1] == '\0')) {
|
||||
format_info_line(line, r_name, r_type);
|
||||
j = 0;
|
||||
memset(line, 0, sizeof(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static daemon_reply _lvmlockd_send(const char *req_name, ...)
|
||||
{
|
||||
va_list ap;
|
||||
daemon_reply repl;
|
||||
daemon_request req;
|
||||
|
||||
req = daemon_request_make(req_name);
|
||||
|
||||
va_start(ap, req_name);
|
||||
daemon_request_extend_v(req, ap);
|
||||
va_end(ap);
|
||||
|
||||
repl = daemon_send(_lvmlockd, req);
|
||||
|
||||
daemon_request_destroy(req);
|
||||
|
||||
return repl;
|
||||
}
|
||||
|
||||
/* See the same in lib/locking/lvmlockd.c */
|
||||
#define NO_LOCKD_RESULT -1000
|
||||
|
||||
static int _lvmlockd_result(daemon_reply reply, int *result)
|
||||
{
|
||||
int reply_result;
|
||||
|
||||
if (reply.error) {
|
||||
log_error("lvmlockd_result reply error %d", reply.error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) {
|
||||
log_error("lvmlockd_result bad response");
|
||||
return 0;
|
||||
}
|
||||
|
||||
reply_result = daemon_reply_int(reply, "op_result", NO_LOCKD_RESULT);
|
||||
if (reply_result == -1000) {
|
||||
log_error("lvmlockd_result no op_result");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*result = reply_result;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_quit(void)
|
||||
{
|
||||
daemon_reply reply;
|
||||
int rv = 0;
|
||||
|
||||
reply = daemon_send_simple(_lvmlockd, "quit", NULL);
|
||||
|
||||
if (reply.error) {
|
||||
log_error("reply error %d", reply.error);
|
||||
rv = reply.error;
|
||||
}
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int setup_dump_socket(void)
|
||||
{
|
||||
int s, rv;
|
||||
|
||||
s = socket(AF_LOCAL, SOCK_DGRAM, 0);
|
||||
if (s < 0)
|
||||
return s;
|
||||
|
||||
memset(&dump_addr, 0, sizeof(dump_addr));
|
||||
dump_addr.sun_family = AF_LOCAL;
|
||||
strcpy(&dump_addr.sun_path[1], DUMP_SOCKET_NAME);
|
||||
dump_addrlen = sizeof(sa_family_t) + strlen(dump_addr.sun_path+1) + 1;
|
||||
|
||||
rv = bind(s, (struct sockaddr *) &dump_addr, dump_addrlen);
|
||||
if (rv < 0) {
|
||||
if (!close(s))
|
||||
log_error("failed to close dump socket");
|
||||
return rv;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int do_dump(const char *req_name)
|
||||
{
|
||||
daemon_reply reply;
|
||||
int result;
|
||||
int fd, rv = 0;
|
||||
|
||||
fd = setup_dump_socket();
|
||||
if (fd < 0) {
|
||||
log_error("socket error %d", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
reply = daemon_send_simple(_lvmlockd, req_name, NULL);
|
||||
|
||||
if (reply.error) {
|
||||
log_error("reply error %d", reply.error);
|
||||
rv = reply.error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = daemon_reply_int(reply, "result", 0);
|
||||
dump_len = daemon_reply_int(reply, "dump_len", 0);
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
|
||||
if (result < 0) {
|
||||
rv = result;
|
||||
log_error("result %d", result);
|
||||
}
|
||||
|
||||
if (!dump_len)
|
||||
goto out;
|
||||
|
||||
memset(dump_buf, 0, sizeof(dump_buf));
|
||||
|
||||
rv = recvfrom(fd, dump_buf, dump_len, MSG_WAITALL,
|
||||
(struct sockaddr *)&dump_addr, &dump_addrlen);
|
||||
if (rv < 0) {
|
||||
log_error("recvfrom error %d %d", rv, errno);
|
||||
rv = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
if ((info && dump) || !strcmp(req_name, "dump"))
|
||||
printf("%s\n", dump_buf);
|
||||
else
|
||||
format_info();
|
||||
out:
|
||||
if (close(fd))
|
||||
log_error("failed to close dump socket %d", fd);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int do_able(const char *req_name)
|
||||
{
|
||||
daemon_reply reply;
|
||||
int result;
|
||||
int rv;
|
||||
|
||||
reply = _lvmlockd_send(req_name,
|
||||
"cmd = %s", "lvmlockctl",
|
||||
"pid = %d", getpid(),
|
||||
"vg_name = %s", arg_vg_name,
|
||||
NULL);
|
||||
|
||||
if (!_lvmlockd_result(reply, &result)) {
|
||||
log_error("lvmlockd result %d", result);
|
||||
rv = result;
|
||||
} else {
|
||||
rv = 0;
|
||||
}
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int do_stop_lockspaces(void)
|
||||
{
|
||||
daemon_reply reply;
|
||||
char opts[32];
|
||||
int result;
|
||||
int rv;
|
||||
|
||||
memset(opts, 0, sizeof(opts));
|
||||
|
||||
if (wait_opt)
|
||||
strcat(opts, "wait ");
|
||||
if (force_opt)
|
||||
strcat(opts, "force ");
|
||||
|
||||
reply = _lvmlockd_send("stop_all",
|
||||
"cmd = %s", "lvmlockctl",
|
||||
"pid = %d", getpid(),
|
||||
"opts = %s", opts[0] ? opts : "none",
|
||||
NULL);
|
||||
|
||||
if (!_lvmlockd_result(reply, &result)) {
|
||||
log_error("lvmlockd result %d", result);
|
||||
rv = result;
|
||||
} else {
|
||||
rv = 0;
|
||||
}
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int do_kill(void)
|
||||
{
|
||||
daemon_reply reply;
|
||||
int result;
|
||||
int rv;
|
||||
|
||||
syslog(LOG_EMERG, "Lost access to sanlock lease storage in VG %s.", arg_vg_name);
|
||||
/* These two lines explain the manual alternative to the FIXME below. */
|
||||
syslog(LOG_EMERG, "Immediately deactivate LVs in VG %s.", arg_vg_name);
|
||||
syslog(LOG_EMERG, "Once VG is unused, run lvmlockctl --drop %s.", arg_vg_name);
|
||||
|
||||
/*
|
||||
* It may not be strictly necessary to notify lvmlockd of the kill, but
|
||||
* lvmlockd can use this information to avoid attempting any new lock
|
||||
* requests in the VG (which would fail anyway), and can return an
|
||||
* error indicating that the VG has been killed.
|
||||
*/
|
||||
|
||||
reply = _lvmlockd_send("kill_vg",
|
||||
"cmd = %s", "lvmlockctl",
|
||||
"pid = %d", getpid(),
|
||||
"vg_name = %s", arg_vg_name,
|
||||
NULL);
|
||||
|
||||
if (!_lvmlockd_result(reply, &result)) {
|
||||
log_error("lvmlockd result %d", result);
|
||||
rv = result;
|
||||
} else {
|
||||
rv = 0;
|
||||
}
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
|
||||
/*
|
||||
* FIXME: here is where we should implement a strong form of
|
||||
* blkdeactivate, and if it completes successfully, automatically call
|
||||
* do_drop() afterward. (The drop step may not always be necessary
|
||||
* if the lvm commands run while shutting things down release all the
|
||||
* leases.)
|
||||
*
|
||||
* run_strong_blkdeactivate();
|
||||
* do_drop();
|
||||
*/
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int do_drop(void)
|
||||
{
|
||||
daemon_reply reply;
|
||||
int result;
|
||||
int rv;
|
||||
|
||||
syslog(LOG_WARNING, "Dropping locks for VG %s.", arg_vg_name);
|
||||
|
||||
/*
|
||||
* Check for misuse by looking for any active LVs in the VG
|
||||
* and refusing this operation if found? One possible way
|
||||
* to kill LVs (e.g. if fs cannot be unmounted) is to suspend
|
||||
* them, or replace them with the error target. In that
|
||||
* case the LV will still appear to be active, but it is
|
||||
* safe to release the lock.
|
||||
*/
|
||||
|
||||
reply = _lvmlockd_send("drop_vg",
|
||||
"cmd = %s", "lvmlockctl",
|
||||
"pid = %d", getpid(),
|
||||
"vg_name = %s", arg_vg_name,
|
||||
NULL);
|
||||
|
||||
if (!_lvmlockd_result(reply, &result)) {
|
||||
log_error("lvmlockd result %d", result);
|
||||
rv = result;
|
||||
} else {
|
||||
rv = 0;
|
||||
}
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
printf("lvmlockctl options\n");
|
||||
printf("Options:\n");
|
||||
printf("--help | -h\n");
|
||||
printf(" Show this help information.\n");
|
||||
printf("--quit | -q\n");
|
||||
printf(" Tell lvmlockd to quit.\n");
|
||||
printf("--info | -i\n");
|
||||
printf(" Print lock state information from lvmlockd.\n");
|
||||
printf("--dump | -d\n");
|
||||
printf(" Print log buffer from lvmlockd.\n");
|
||||
printf("--wait | -w 0|1\n");
|
||||
printf(" Wait option for other commands.\n");
|
||||
printf("--force | -f 0|1>\n");
|
||||
printf(" Force option for other commands.\n");
|
||||
printf("--kill | -k <vg_name>\n");
|
||||
printf(" Kill access to the vg when sanlock cannot renew lease.\n");
|
||||
printf("--drop | -r <vg_name>\n");
|
||||
printf(" Clear locks for the vg after it has been killed and is no longer used.\n");
|
||||
printf("--gl-enable <vg_name>\n");
|
||||
printf(" Tell lvmlockd to enable the global lock in a sanlock vg.\n");
|
||||
printf("--gl-disable <vg_name>\n");
|
||||
printf(" Tell lvmlockd to disable the global lock in a sanlock vg.\n");
|
||||
printf("--stop-lockspaces | -S\n");
|
||||
printf(" Stop all lockspaces.\n");
|
||||
}
|
||||
|
||||
static int read_options(int argc, char *argv[])
|
||||
{
|
||||
int option_index = 0;
|
||||
int c;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"help", no_argument, 0, 'h' },
|
||||
{"quit", no_argument, 0, 'q' },
|
||||
{"info", no_argument, 0, 'i' },
|
||||
{"dump", no_argument, 0, 'd' },
|
||||
{"wait", required_argument, 0, 'w' },
|
||||
{"force", required_argument, 0, 'f' },
|
||||
{"kill", required_argument, 0, 'k' },
|
||||
{"drop", required_argument, 0, 'r' },
|
||||
{"gl-enable", required_argument, 0, 'E' },
|
||||
{"gl-disable", required_argument, 0, 'D' },
|
||||
{"stop-lockspaces", no_argument, 0, 'S' },
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
if (argc == 1) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
c = getopt_long(argc, argv, "hqidE:D:w:k:r:S", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
/* --help */
|
||||
print_usage();
|
||||
exit(0);
|
||||
case 'q':
|
||||
/* --quit */
|
||||
quit = 1;
|
||||
break;
|
||||
case 'i':
|
||||
/* --info */
|
||||
info = 1;
|
||||
break;
|
||||
case 'd':
|
||||
/* --dump */
|
||||
dump = 1;
|
||||
break;
|
||||
case 'w':
|
||||
wait_opt = atoi(optarg);
|
||||
break;
|
||||
case 'k':
|
||||
kill_vg = 1;
|
||||
arg_vg_name = strdup(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
drop_vg = 1;
|
||||
arg_vg_name = strdup(optarg);
|
||||
break;
|
||||
case 'E':
|
||||
gl_enable = 1;
|
||||
arg_vg_name = strdup(optarg);
|
||||
break;
|
||||
case 'D':
|
||||
gl_disable = 1;
|
||||
arg_vg_name = strdup(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
stop_lockspaces = 1;
|
||||
break;
|
||||
default:
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rv = 0;
|
||||
|
||||
rv = read_options(argc, argv);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
_lvmlockd = lvmlockd_open(NULL);
|
||||
|
||||
if (_lvmlockd.socket_fd < 0 || _lvmlockd.error) {
|
||||
log_error("Cannot connect to lvmlockd.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (quit) {
|
||||
rv = do_quit();
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (info) {
|
||||
rv = do_dump("info");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dump) {
|
||||
rv = do_dump("dump");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kill_vg) {
|
||||
rv = do_kill();
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (drop_vg) {
|
||||
rv = do_drop();
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (gl_enable) {
|
||||
rv = do_able("enable_gl");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (gl_disable) {
|
||||
rv = do_able("disable_gl");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (stop_lockspaces) {
|
||||
rv = do_stop_lockspaces();
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
lvmlockd_close(_lvmlockd);
|
||||
return rv;
|
||||
}
|
||||
51
daemons/lvmlockd/lvmlockd-client.h
Normal file
51
daemons/lvmlockd/lvmlockd-client.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LVMLOCKD_CLIENT_H
|
||||
#define _LVM_LVMLOCKD_CLIENT_H
|
||||
|
||||
#include "daemon-client.h"
|
||||
|
||||
#define LVMLOCKD_SOCKET DEFAULT_RUN_DIR "/lvmlockd.socket"
|
||||
|
||||
/* Wrappers to open/close connection */
|
||||
|
||||
static inline daemon_handle lvmlockd_open(const char *sock)
|
||||
{
|
||||
daemon_info lvmlockd_info = {
|
||||
.path = "lvmlockd",
|
||||
.socket = sock ?: LVMLOCKD_SOCKET,
|
||||
.protocol = "lvmlockd",
|
||||
.protocol_version = 1,
|
||||
.autostart = 0
|
||||
};
|
||||
|
||||
return daemon_open(lvmlockd_info);
|
||||
}
|
||||
|
||||
static inline void lvmlockd_close(daemon_handle h)
|
||||
{
|
||||
return daemon_close(h);
|
||||
}
|
||||
|
||||
/*
|
||||
* Errors returned as the lvmlockd result value.
|
||||
*/
|
||||
#define ENOLS 210 /* lockspace not found */
|
||||
#define ESTARTING 211 /* lockspace is starting */
|
||||
#define EARGS 212
|
||||
#define EHOSTID 213
|
||||
#define EMANAGER 214
|
||||
#define EPREPARE 215
|
||||
#define ELOCKD 216
|
||||
#define EVGKILLED 217 /* sanlock lost access to leases and VG is killed. */
|
||||
#define ELOCKIO 218 /* sanlock io errors during lock op, may be transient. */
|
||||
|
||||
#endif /* _LVM_LVMLOCKD_CLIENT_H */
|
||||
5844
daemons/lvmlockd/lvmlockd-core.c
Normal file
5844
daemons/lvmlockd/lvmlockd-core.c
Normal file
File diff suppressed because it is too large
Load Diff
662
daemons/lvmlockd/lvmlockd-dlm.c
Normal file
662
daemons/lvmlockd/lvmlockd-dlm.c
Normal file
@@ -0,0 +1,662 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define _XOPEN_SOURCE 500 /* pthread */
|
||||
#define _ISOC99_SOURCE
|
||||
|
||||
#include "tool.h"
|
||||
|
||||
#include "daemon-server.h"
|
||||
#include "daemon-log.h"
|
||||
#include "xlate.h"
|
||||
|
||||
#include "lvmlockd-internal.h"
|
||||
#include "lvmlockd-client.h"
|
||||
|
||||
/*
|
||||
* Using synchronous _wait dlm apis so do not define _REENTRANT and
|
||||
* link with non-threaded version of library, libdlm_lt.
|
||||
*/
|
||||
#include "libdlm.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <byteswap.h>
|
||||
#include <syslog.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
struct lm_dlm {
|
||||
dlm_lshandle_t *dh;
|
||||
};
|
||||
|
||||
struct rd_dlm {
|
||||
struct dlm_lksb lksb;
|
||||
struct val_blk *vb;
|
||||
};
|
||||
|
||||
int lm_data_size_dlm(void)
|
||||
{
|
||||
return sizeof(struct rd_dlm);
|
||||
}
|
||||
|
||||
/*
|
||||
* lock_args format
|
||||
*
|
||||
* vg_lock_args format for dlm is
|
||||
* vg_version_string:undefined:cluster_name
|
||||
*
|
||||
* lv_lock_args are not used for dlm
|
||||
*
|
||||
* version_string is MAJOR.MINOR.PATCH
|
||||
* undefined may contain ":"
|
||||
*/
|
||||
|
||||
#define VG_LOCK_ARGS_MAJOR 1
|
||||
#define VG_LOCK_ARGS_MINOR 0
|
||||
#define VG_LOCK_ARGS_PATCH 0
|
||||
|
||||
static int cluster_name_from_args(char *vg_args, char *clustername)
|
||||
{
|
||||
return last_string_from_args(vg_args, clustername);
|
||||
}
|
||||
|
||||
static int check_args_version(char *vg_args)
|
||||
{
|
||||
unsigned int major = 0;
|
||||
int rv;
|
||||
|
||||
rv = version_from_args(vg_args, &major, NULL, NULL);
|
||||
if (rv < 0) {
|
||||
log_error("check_args_version %s error %d", vg_args, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (major > VG_LOCK_ARGS_MAJOR) {
|
||||
log_error("check_args_version %s major %d %d", vg_args, major, VG_LOCK_ARGS_MAJOR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This will be set after dlm_controld is started. */
|
||||
#define DLM_CLUSTER_NAME_PATH "/sys/kernel/config/dlm/cluster/cluster_name"
|
||||
|
||||
static int read_cluster_name(char *clustername)
|
||||
{
|
||||
static const char close_error_msg[] = "read_cluster_name: close_error %d";
|
||||
char *n;
|
||||
int fd;
|
||||
int rv;
|
||||
|
||||
if (daemon_test) {
|
||||
sprintf(clustername, "%s", "test");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(DLM_CLUSTER_NAME_PATH, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
log_debug("read_cluster_name: open error %d, check dlm_controld", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
rv = read(fd, clustername, MAX_ARGS);
|
||||
if (rv < 0) {
|
||||
log_error("read_cluster_name: cluster name read error %d, check dlm_controld", fd);
|
||||
if (close(fd))
|
||||
log_error(close_error_msg, fd);
|
||||
return rv;
|
||||
}
|
||||
|
||||
n = strstr(clustername, "\n");
|
||||
if (n)
|
||||
*n = '\0';
|
||||
if (close(fd))
|
||||
log_error(close_error_msg, fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm_init_vg_dlm(char *ls_name, char *vg_name, uint32_t flags, char *vg_args)
|
||||
{
|
||||
char clustername[MAX_ARGS+1];
|
||||
char lock_args_version[MAX_ARGS+1];
|
||||
int rv;
|
||||
|
||||
memset(clustername, 0, sizeof(clustername));
|
||||
memset(lock_args_version, 0, sizeof(lock_args_version));
|
||||
|
||||
snprintf(lock_args_version, MAX_ARGS, "%u.%u.%u",
|
||||
VG_LOCK_ARGS_MAJOR, VG_LOCK_ARGS_MINOR, VG_LOCK_ARGS_PATCH);
|
||||
|
||||
rv = read_cluster_name(clustername);
|
||||
if (rv < 0)
|
||||
return -EMANAGER;
|
||||
|
||||
if (strlen(clustername) + strlen(lock_args_version) + 2 > MAX_ARGS) {
|
||||
log_error("init_vg_dlm args too long");
|
||||
return -EARGS;
|
||||
}
|
||||
|
||||
snprintf(vg_args, MAX_ARGS, "%s:%s", lock_args_version, clustername);
|
||||
rv = 0;
|
||||
|
||||
log_debug("init_vg_dlm done %s vg_args %s", ls_name, vg_args);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int lm_prepare_lockspace_dlm(struct lockspace *ls)
|
||||
{
|
||||
char sys_clustername[MAX_ARGS+1];
|
||||
char arg_clustername[MAX_ARGS+1];
|
||||
struct lm_dlm *lmd;
|
||||
int rv;
|
||||
|
||||
memset(sys_clustername, 0, sizeof(sys_clustername));
|
||||
memset(arg_clustername, 0, sizeof(arg_clustername));
|
||||
|
||||
rv = read_cluster_name(sys_clustername);
|
||||
if (rv < 0)
|
||||
return -EMANAGER;
|
||||
|
||||
if (!ls->vg_args[0]) {
|
||||
/* global lockspace has no vg args */
|
||||
goto skip_args;
|
||||
}
|
||||
|
||||
rv = check_args_version(ls->vg_args);
|
||||
if (rv < 0)
|
||||
return -EARGS;
|
||||
|
||||
rv = cluster_name_from_args(ls->vg_args, arg_clustername);
|
||||
if (rv < 0) {
|
||||
log_error("prepare_lockspace_dlm %s no cluster name from args %s", ls->name, ls->vg_args);
|
||||
return -EARGS;
|
||||
}
|
||||
|
||||
if (strcmp(sys_clustername, arg_clustername)) {
|
||||
log_error("prepare_lockspace_dlm %s mismatching cluster names sys %s arg %s",
|
||||
ls->name, sys_clustername, arg_clustername);
|
||||
return -EARGS;
|
||||
}
|
||||
|
||||
skip_args:
|
||||
lmd = malloc(sizeof(struct lm_dlm));
|
||||
if (!lmd)
|
||||
return -ENOMEM;
|
||||
|
||||
ls->lm_data = lmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm_add_lockspace_dlm(struct lockspace *ls, int adopt)
|
||||
{
|
||||
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
||||
|
||||
if (daemon_test)
|
||||
return 0;
|
||||
|
||||
if (adopt)
|
||||
lmd->dh = dlm_open_lockspace(ls->name);
|
||||
else
|
||||
lmd->dh = dlm_new_lockspace(ls->name, 0600, DLM_LSFL_NEWEXCL);
|
||||
|
||||
if (!lmd->dh) {
|
||||
log_error("add_lockspace_dlm %s adopt %d error", ls->name, adopt);
|
||||
free(lmd);
|
||||
ls->lm_data = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm_rem_lockspace_dlm(struct lockspace *ls, int free_vg)
|
||||
{
|
||||
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
||||
int rv;
|
||||
|
||||
if (daemon_test)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If free_vg is set, it means we are doing vgremove, and we may want
|
||||
* to tell any other nodes to leave the lockspace. This is not really
|
||||
* necessary since there should be no harm in having an unused
|
||||
* lockspace sitting around. A new "notification lock" would need to
|
||||
* be added with a callback to signal this.
|
||||
*/
|
||||
|
||||
rv = dlm_release_lockspace(ls->name, lmd->dh, 1);
|
||||
if (rv < 0) {
|
||||
log_error("rem_lockspace_dlm error %d", rv);
|
||||
return rv;
|
||||
}
|
||||
out:
|
||||
free(lmd);
|
||||
ls->lm_data = NULL;
|
||||
|
||||
if (!strcmp(ls->name, gl_lsname_dlm))
|
||||
gl_running_dlm = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm_add_resource_dlm(struct lockspace *ls, struct resource *r, int with_lock_nl)
|
||||
{
|
||||
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
||||
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
||||
uint32_t flags = 0;
|
||||
char *buf;
|
||||
int rv;
|
||||
|
||||
if (r->type == LD_RT_GL || r->type == LD_RT_VG) {
|
||||
buf = malloc(sizeof(struct val_blk) + DLM_LVB_LEN);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
memset(buf, 0, sizeof(struct val_blk) + DLM_LVB_LEN);
|
||||
|
||||
rdd->vb = (struct val_blk *)buf;
|
||||
rdd->lksb.sb_lvbptr = buf + sizeof(struct val_blk);
|
||||
|
||||
flags |= LKF_VALBLK;
|
||||
}
|
||||
|
||||
if (!with_lock_nl)
|
||||
goto out;
|
||||
|
||||
/* because this is a new NL lock request */
|
||||
flags |= LKF_EXPEDITE;
|
||||
|
||||
if (daemon_test)
|
||||
goto out;
|
||||
|
||||
rv = dlm_ls_lock_wait(lmd->dh, LKM_NLMODE, &rdd->lksb, flags,
|
||||
r->name, strlen(r->name),
|
||||
0, NULL, NULL, NULL);
|
||||
if (rv < 0) {
|
||||
log_error("S %s R %s add_resource_dlm lock error %d", ls->name, r->name, rv);
|
||||
return rv;
|
||||
}
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm_rem_resource_dlm(struct lockspace *ls, struct resource *r)
|
||||
{
|
||||
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
||||
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
||||
struct dlm_lksb *lksb;
|
||||
int rv = 0;
|
||||
|
||||
if (daemon_test)
|
||||
goto out;
|
||||
|
||||
lksb = &rdd->lksb;
|
||||
|
||||
if (!lksb->sb_lkid)
|
||||
goto out;
|
||||
|
||||
rv = dlm_ls_unlock_wait(lmd->dh, lksb->sb_lkid, 0, lksb);
|
||||
if (rv < 0) {
|
||||
log_error("S %s R %s rem_resource_dlm unlock error %d", ls->name, r->name, rv);
|
||||
}
|
||||
out:
|
||||
if (rdd->vb)
|
||||
free(rdd->vb);
|
||||
|
||||
memset(rdd, 0, sizeof(struct rd_dlm));
|
||||
r->lm_init = 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int to_dlm_mode(int ld_mode)
|
||||
{
|
||||
switch (ld_mode) {
|
||||
case LD_LK_EX:
|
||||
return LKM_EXMODE;
|
||||
case LD_LK_SH:
|
||||
return LKM_PRMODE;
|
||||
};
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int lm_adopt_dlm(struct lockspace *ls, struct resource *r, int ld_mode,
|
||||
uint32_t *r_version)
|
||||
{
|
||||
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
||||
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
||||
struct dlm_lksb *lksb;
|
||||
uint32_t flags = 0;
|
||||
int mode;
|
||||
int rv;
|
||||
|
||||
*r_version = 0;
|
||||
|
||||
if (!r->lm_init) {
|
||||
rv = lm_add_resource_dlm(ls, r, 0);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
r->lm_init = 1;
|
||||
}
|
||||
|
||||
lksb = &rdd->lksb;
|
||||
|
||||
flags |= LKF_PERSISTENT;
|
||||
flags |= LKF_ORPHAN;
|
||||
|
||||
if (rdd->vb)
|
||||
flags |= LKF_VALBLK;
|
||||
|
||||
mode = to_dlm_mode(ld_mode);
|
||||
if (mode < 0) {
|
||||
log_error("adopt_dlm invalid mode %d", ld_mode);
|
||||
rv = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_debug("S %s R %s adopt_dlm", ls->name, r->name);
|
||||
|
||||
if (daemon_test)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* dlm returns 0 for success, -EAGAIN if an orphan is
|
||||
* found with another mode, and -ENOENT if no orphan.
|
||||
*
|
||||
* cast/bast/param are (void *)1 because the kernel
|
||||
* returns errors if some are null.
|
||||
*/
|
||||
|
||||
rv = dlm_ls_lockx(lmd->dh, mode, lksb, flags,
|
||||
r->name, strlen(r->name), 0,
|
||||
(void *)1, (void *)1, (void *)1,
|
||||
NULL, NULL);
|
||||
|
||||
if (rv == -EAGAIN) {
|
||||
log_debug("S %s R %s adopt_dlm adopt mode %d try other mode",
|
||||
ls->name, r->name, ld_mode);
|
||||
rv = -EUCLEAN;
|
||||
goto fail;
|
||||
}
|
||||
if (rv < 0) {
|
||||
log_debug("S %s R %s adopt_dlm mode %d flags %x error %d errno %d",
|
||||
ls->name, r->name, mode, flags, rv, errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: For GL/VG locks we probably want to read the lvb,
|
||||
* especially if adopting an ex lock, because when we
|
||||
* release this adopted ex lock we may want to write new
|
||||
* lvb values based on the current lvb values (at lease
|
||||
* in the GL case where we increment the current values.)
|
||||
*
|
||||
* It should be possible to read the lvb by requesting
|
||||
* this lock in the same mode it's already in.
|
||||
*/
|
||||
|
||||
return rv;
|
||||
|
||||
fail:
|
||||
lm_rem_resource_dlm(ls, r);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use PERSISTENT so that if lvmlockd exits while holding locks,
|
||||
* the locks will remain orphaned in the dlm, still protecting what
|
||||
* they were acquired to protect.
|
||||
*/
|
||||
|
||||
int lm_lock_dlm(struct lockspace *ls, struct resource *r, int ld_mode,
|
||||
uint32_t *r_version, int adopt)
|
||||
{
|
||||
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
||||
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
||||
struct dlm_lksb *lksb;
|
||||
struct val_blk vb;
|
||||
uint32_t flags = 0;
|
||||
uint16_t vb_version;
|
||||
int mode;
|
||||
int rv;
|
||||
|
||||
if (adopt) {
|
||||
/* When adopting, we don't follow the normal method
|
||||
of acquiring a NL lock then converting it to the
|
||||
desired mode. */
|
||||
return lm_adopt_dlm(ls, r, ld_mode, r_version);
|
||||
}
|
||||
|
||||
if (!r->lm_init) {
|
||||
rv = lm_add_resource_dlm(ls, r, 1);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
r->lm_init = 1;
|
||||
}
|
||||
|
||||
lksb = &rdd->lksb;
|
||||
|
||||
flags |= LKF_CONVERT;
|
||||
flags |= LKF_NOQUEUE;
|
||||
flags |= LKF_PERSISTENT;
|
||||
|
||||
if (rdd->vb)
|
||||
flags |= LKF_VALBLK;
|
||||
|
||||
mode = to_dlm_mode(ld_mode);
|
||||
if (mode < 0) {
|
||||
log_error("lock_dlm invalid mode %d", ld_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
log_debug("S %s R %s lock_dlm", ls->name, r->name);
|
||||
|
||||
if (daemon_test) {
|
||||
*r_version = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = dlm_ls_lock_wait(lmd->dh, mode, lksb, flags,
|
||||
r->name, strlen(r->name),
|
||||
0, NULL, NULL, NULL);
|
||||
if (rv == -EAGAIN) {
|
||||
log_error("S %s R %s lock_dlm mode %d rv EAGAIN", ls->name, r->name, mode);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (rv < 0) {
|
||||
log_error("S %s R %s lock_dlm error %d", ls->name, r->name, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (rdd->vb) {
|
||||
if (lksb->sb_flags & DLM_SBF_VALNOTVALID) {
|
||||
log_debug("S %s R %s lock_dlm VALNOTVALID", ls->name, r->name);
|
||||
memset(rdd->vb, 0, sizeof(struct val_blk));
|
||||
*r_version = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&vb, lksb->sb_lvbptr, sizeof(struct val_blk));
|
||||
vb_version = le16_to_cpu(vb.version);
|
||||
|
||||
if (vb_version && ((vb_version & 0xFF00) > (VAL_BLK_VERSION & 0xFF00))) {
|
||||
log_error("S %s R %s lock_dlm ignore vb_version %x",
|
||||
ls->name, r->name, vb_version);
|
||||
*r_version = 0;
|
||||
free(rdd->vb);
|
||||
rdd->vb = NULL;
|
||||
lksb->sb_lvbptr = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*r_version = le32_to_cpu(vb.r_version);
|
||||
memcpy(rdd->vb, &vb, sizeof(vb)); /* rdd->vb saved as le */
|
||||
|
||||
log_debug("S %s R %s lock_dlm get r_version %u",
|
||||
ls->name, r->name, *r_version);
|
||||
}
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm_convert_dlm(struct lockspace *ls, struct resource *r,
|
||||
int ld_mode, uint32_t r_version)
|
||||
{
|
||||
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
||||
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
||||
struct dlm_lksb *lksb = &rdd->lksb;
|
||||
uint32_t mode;
|
||||
uint32_t flags = 0;
|
||||
int rv;
|
||||
|
||||
log_debug("S %s R %s convert_dlm", ls->name, r->name);
|
||||
|
||||
flags |= LKF_CONVERT;
|
||||
flags |= LKF_NOQUEUE;
|
||||
flags |= LKF_PERSISTENT;
|
||||
|
||||
if (rdd->vb && r_version && (r->mode == LD_LK_EX)) {
|
||||
if (!rdd->vb->version) {
|
||||
/* first time vb has been written */
|
||||
rdd->vb->version = cpu_to_le16(VAL_BLK_VERSION);
|
||||
}
|
||||
rdd->vb->r_version = cpu_to_le32(r_version);
|
||||
memcpy(lksb->sb_lvbptr, rdd->vb, sizeof(struct val_blk));
|
||||
|
||||
log_debug("S %s R %s convert_dlm set r_version %u",
|
||||
ls->name, r->name, r_version);
|
||||
|
||||
flags |= LKF_VALBLK;
|
||||
}
|
||||
|
||||
mode = to_dlm_mode(ld_mode);
|
||||
|
||||
if (daemon_test)
|
||||
return 0;
|
||||
|
||||
rv = dlm_ls_lock_wait(lmd->dh, mode, lksb, flags,
|
||||
r->name, strlen(r->name),
|
||||
0, NULL, NULL, NULL);
|
||||
if (rv == -EAGAIN) {
|
||||
/* FIXME: When does this happen? Should something different be done? */
|
||||
log_error("S %s R %s convert_dlm mode %d rv EAGAIN", ls->name, r->name, mode);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (rv < 0) {
|
||||
log_error("S %s R %s convert_dlm error %d", ls->name, r->name, rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int lm_unlock_dlm(struct lockspace *ls, struct resource *r,
|
||||
uint32_t r_version, uint32_t lmuf_flags)
|
||||
{
|
||||
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
||||
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
||||
struct dlm_lksb *lksb = &rdd->lksb;
|
||||
uint32_t flags = 0;
|
||||
int rv;
|
||||
|
||||
log_debug("S %s R %s unlock_dlm r_version %u flags %x",
|
||||
ls->name, r->name, r_version, lmuf_flags);
|
||||
|
||||
/*
|
||||
* Do not set PERSISTENT, because we don't need an orphan
|
||||
* NL lock to protect anything.
|
||||
*/
|
||||
|
||||
flags |= LKF_CONVERT;
|
||||
|
||||
if (rdd->vb && r_version && (r->mode == LD_LK_EX)) {
|
||||
if (!rdd->vb->version) {
|
||||
/* first time vb has been written */
|
||||
rdd->vb->version = cpu_to_le16(VAL_BLK_VERSION);
|
||||
}
|
||||
if (r_version)
|
||||
rdd->vb->r_version = cpu_to_le32(r_version);
|
||||
memcpy(lksb->sb_lvbptr, rdd->vb, sizeof(struct val_blk));
|
||||
|
||||
log_debug("S %s R %s unlock_dlm set r_version %u",
|
||||
ls->name, r->name, r_version);
|
||||
|
||||
flags |= LKF_VALBLK;
|
||||
}
|
||||
|
||||
if (daemon_test)
|
||||
return 0;
|
||||
|
||||
rv = dlm_ls_lock_wait(lmd->dh, LKM_NLMODE, lksb, flags,
|
||||
r->name, strlen(r->name),
|
||||
0, NULL, NULL, NULL);
|
||||
if (rv < 0) {
|
||||
log_error("S %s R %s unlock_dlm error %d", ls->name, r->name, rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* This list could be read from dlm_controld via libdlmcontrol,
|
||||
* but it's simpler to get it from sysfs.
|
||||
*/
|
||||
|
||||
#define DLM_LOCKSPACES_PATH "/sys/kernel/config/dlm/cluster/spaces"
|
||||
|
||||
int lm_get_lockspaces_dlm(struct list_head *ls_rejoin)
|
||||
{
|
||||
static const char closedir_err_msg[] = "lm_get_lockspace_dlm: closedir failed";
|
||||
struct lockspace *ls;
|
||||
struct dirent *de;
|
||||
DIR *ls_dir;
|
||||
|
||||
if (!(ls_dir = opendir(DLM_LOCKSPACES_PATH)))
|
||||
return -ECONNREFUSED;
|
||||
|
||||
while ((de = readdir(ls_dir))) {
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if (strncmp(de->d_name, LVM_LS_PREFIX, strlen(LVM_LS_PREFIX)))
|
||||
continue;
|
||||
|
||||
if (!(ls = alloc_lockspace())) {
|
||||
if (closedir(ls_dir))
|
||||
log_error(closedir_err_msg);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ls->lm_type = LD_LM_DLM;
|
||||
strncpy(ls->name, de->d_name, MAX_NAME);
|
||||
strncpy(ls->vg_name, ls->name + strlen(LVM_LS_PREFIX), MAX_NAME);
|
||||
list_add_tail(&ls->list, ls_rejoin);
|
||||
}
|
||||
|
||||
if (closedir(ls_dir))
|
||||
log_error(closedir_err_msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm_is_running_dlm(void)
|
||||
{
|
||||
char sys_clustername[MAX_ARGS+1];
|
||||
int rv;
|
||||
|
||||
memset(sys_clustername, 0, sizeof(sys_clustername));
|
||||
|
||||
rv = read_cluster_name(sys_clustername);
|
||||
if (rv < 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
577
daemons/lvmlockd/lvmlockd-internal.h
Normal file
577
daemons/lvmlockd/lvmlockd-internal.h
Normal file
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LVMLOCKD_INTERNAL_H
|
||||
#define _LVM_LVMLOCKD_INTERNAL_H
|
||||
|
||||
#define MAX_NAME 64
|
||||
#define MAX_ARGS 64
|
||||
|
||||
#define R_NAME_GL_DISABLED "_GLLK_disabled"
|
||||
#define R_NAME_GL "GLLK"
|
||||
#define R_NAME_VG "VGLK"
|
||||
#define S_NAME_GL_DLM "lvm_global"
|
||||
#define LVM_LS_PREFIX "lvm_" /* ls name is prefix + vg_name */
|
||||
/* global lockspace name for sanlock is a vg name */
|
||||
|
||||
/* lock manager types */
|
||||
enum {
|
||||
LD_LM_NONE = 0,
|
||||
LD_LM_UNUSED = 1, /* place holder so values match lib/locking/lvmlockd.h */
|
||||
LD_LM_DLM = 2,
|
||||
LD_LM_SANLOCK = 3,
|
||||
};
|
||||
|
||||
/* operation types */
|
||||
enum {
|
||||
LD_OP_HELLO = 1,
|
||||
LD_OP_QUIT,
|
||||
LD_OP_INIT,
|
||||
LD_OP_FREE,
|
||||
LD_OP_START,
|
||||
LD_OP_STOP,
|
||||
LD_OP_LOCK,
|
||||
LD_OP_UPDATE,
|
||||
LD_OP_CLOSE,
|
||||
LD_OP_ENABLE,
|
||||
LD_OP_DISABLE,
|
||||
LD_OP_START_WAIT,
|
||||
LD_OP_STOP_ALL,
|
||||
LD_OP_DUMP_INFO,
|
||||
LD_OP_DUMP_LOG,
|
||||
LD_OP_RENAME_BEFORE,
|
||||
LD_OP_RENAME_FINAL,
|
||||
LD_OP_RUNNING_LM,
|
||||
LD_OP_FIND_FREE_LOCK,
|
||||
LD_OP_FORGET_VG_NAME,
|
||||
LD_OP_KILL_VG,
|
||||
LD_OP_DROP_VG,
|
||||
};
|
||||
|
||||
/* resource types */
|
||||
enum {
|
||||
LD_RT_GL = 1,
|
||||
LD_RT_VG,
|
||||
LD_RT_LV,
|
||||
};
|
||||
|
||||
/* lock modes, more restrictive must be larger value */
|
||||
enum {
|
||||
LD_LK_IV = -1,
|
||||
LD_LK_UN = 0,
|
||||
LD_LK_NL = 1,
|
||||
LD_LK_SH = 2,
|
||||
LD_LK_EX = 3,
|
||||
};
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
struct client {
|
||||
struct list_head list;
|
||||
pthread_mutex_t mutex;
|
||||
int pid;
|
||||
int fd;
|
||||
int pi;
|
||||
uint32_t id;
|
||||
unsigned int recv : 1;
|
||||
unsigned int dead : 1;
|
||||
unsigned int poll_ignore : 1;
|
||||
char name[MAX_NAME+1];
|
||||
};
|
||||
|
||||
#define LD_AF_PERSISTENT 0x00000001
|
||||
#define LD_AF_UNUSED 0x00000002 /* use me */
|
||||
#define LD_AF_UNLOCK_CANCEL 0x00000004
|
||||
#define LD_AF_NEXT_VERSION 0x00000008
|
||||
#define LD_AF_WAIT 0x00000010
|
||||
#define LD_AF_FORCE 0x00000020
|
||||
#define LD_AF_EX_DISABLE 0x00000040
|
||||
#define LD_AF_ENABLE 0x00000080
|
||||
#define LD_AF_DISABLE 0x00000100
|
||||
#define LD_AF_SEARCH_LS 0x00000200
|
||||
#define LD_AF_WAIT_STARTING 0x00001000
|
||||
#define LD_AF_DUP_GL_LS 0x00002000
|
||||
#define LD_AF_INACTIVE_LS 0x00004000
|
||||
#define LD_AF_ADD_LS_ERROR 0x00008000
|
||||
#define LD_AF_ADOPT 0x00010000
|
||||
#define LD_AF_WARN_GL_REMOVED 0x00020000
|
||||
|
||||
/*
|
||||
* Number of times to repeat a lock request after
|
||||
* a lock conflict (-EAGAIN) if unspecified in the
|
||||
* request.
|
||||
*/
|
||||
#define DEFAULT_MAX_RETRIES 4
|
||||
|
||||
struct action {
|
||||
struct list_head list;
|
||||
uint32_t client_id;
|
||||
uint32_t flags; /* LD_AF_ */
|
||||
uint32_t version;
|
||||
uint64_t host_id;
|
||||
int8_t op; /* operation type LD_OP_ */
|
||||
int8_t rt; /* resource type LD_RT_ */
|
||||
int8_t mode; /* lock mode LD_LK_ */
|
||||
int8_t lm_type; /* lock manager: LM_DLM, LM_SANLOCK */
|
||||
int retries;
|
||||
int max_retries;
|
||||
int result;
|
||||
int lm_rv; /* return value from lm_ function */
|
||||
char vg_uuid[64];
|
||||
char vg_name[MAX_NAME+1];
|
||||
char lv_name[MAX_NAME+1];
|
||||
char lv_uuid[MAX_NAME+1];
|
||||
char vg_args[MAX_ARGS+1];
|
||||
char lv_args[MAX_ARGS+1];
|
||||
char vg_sysid[MAX_NAME+1];
|
||||
};
|
||||
|
||||
struct resource {
|
||||
struct list_head list; /* lockspace.resources */
|
||||
char name[MAX_NAME+1]; /* vg name or lv name */
|
||||
int8_t type; /* resource type LD_RT_ */
|
||||
int8_t mode;
|
||||
unsigned int sh_count; /* number of sh locks on locks list */
|
||||
uint32_t version;
|
||||
unsigned int lm_init : 1; /* lm_data is initialized */
|
||||
unsigned int adopt : 1; /* temp flag in remove_inactive_lvs */
|
||||
unsigned int version_zero_valid : 1;
|
||||
struct list_head locks;
|
||||
struct list_head actions;
|
||||
struct val_blk *vb;
|
||||
char lv_args[MAX_ARGS+1];
|
||||
char lm_data[0]; /* lock manager specific data */
|
||||
};
|
||||
|
||||
#define LD_LF_PERSISTENT 0x00000001
|
||||
|
||||
struct lock {
|
||||
struct list_head list; /* resource.locks */
|
||||
int8_t mode; /* lock mode LD_LK_ */
|
||||
uint32_t version;
|
||||
uint32_t flags; /* LD_LF_ */
|
||||
uint32_t client_id; /* may be 0 for persistent or internal locks */
|
||||
};
|
||||
|
||||
struct lockspace {
|
||||
struct list_head list; /* lockspaces */
|
||||
char name[MAX_NAME+1];
|
||||
char vg_name[MAX_NAME+1];
|
||||
char vg_uuid[64];
|
||||
char vg_args[MAX_ARGS+1]; /* lock manager specific args */
|
||||
char vg_sysid[MAX_NAME+1];
|
||||
int8_t lm_type; /* lock manager: LM_DLM, LM_SANLOCK */
|
||||
void *lm_data;
|
||||
uint64_t host_id;
|
||||
uint64_t free_lock_offset; /* start search for free lock here */
|
||||
|
||||
uint32_t start_client_id; /* client_id that started the lockspace */
|
||||
pthread_t thread; /* makes synchronous lock requests */
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t mutex;
|
||||
unsigned int create_fail : 1;
|
||||
unsigned int create_done : 1;
|
||||
unsigned int thread_work : 1;
|
||||
unsigned int thread_stop : 1;
|
||||
unsigned int thread_done : 1;
|
||||
unsigned int sanlock_gl_enabled: 1;
|
||||
unsigned int sanlock_gl_dup: 1;
|
||||
unsigned int free_vg: 1;
|
||||
unsigned int kill_vg: 1;
|
||||
unsigned int drop_vg: 1;
|
||||
|
||||
struct list_head actions; /* new client actions */
|
||||
struct list_head resources; /* resource/lock state for gl/vg/lv */
|
||||
};
|
||||
|
||||
#define VAL_BLK_VERSION 0x0101
|
||||
|
||||
struct val_blk {
|
||||
uint16_t version;
|
||||
uint16_t flags;
|
||||
uint32_t r_version;
|
||||
};
|
||||
|
||||
/* lm_unlock flags */
|
||||
#define LMUF_FREE_VG 0x00000001
|
||||
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
|
||||
static inline void INIT_LIST_HEAD(struct list_head *list)
|
||||
{
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
static inline void __list_del(struct list_head *prev, struct list_head *next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
}
|
||||
|
||||
static inline int list_empty(const struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
list_entry((ptr)->next, type, member)
|
||||
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
|
||||
/* to improve readability */
|
||||
#define WAIT 1
|
||||
#define NO_WAIT 0
|
||||
#define FORCE 1
|
||||
#define NO_FORCE 0
|
||||
|
||||
/*
|
||||
* global variables
|
||||
*/
|
||||
|
||||
#ifndef EXTERN
|
||||
#define EXTERN extern
|
||||
#define INIT(X)
|
||||
#else
|
||||
#undef EXTERN
|
||||
#define EXTERN
|
||||
#define INIT(X) =X
|
||||
#endif
|
||||
|
||||
/*
|
||||
* gl_type_static and gl_use_ are set by command line or config file
|
||||
* to specify whether the global lock comes from dlm or sanlock.
|
||||
* Without a static setting, lvmlockd will figure out where the
|
||||
* global lock should be (but it could get mixed up in cases where
|
||||
* both sanlock and dlm vgs exist.)
|
||||
*
|
||||
* gl_use_dlm means that the gl should come from lockspace gl_lsname_dlm
|
||||
* gl_use_sanlock means that the gl should come from lockspace gl_lsname_sanlock
|
||||
*
|
||||
* gl_use_dlm has precedence over gl_use_sanlock, so if a node sees both
|
||||
* dlm and sanlock vgs, it will use the dlm gl.
|
||||
*
|
||||
* gl_use_ is set when the first evidence of that lm_type is seen
|
||||
* in any command.
|
||||
*
|
||||
* gl_lsname_sanlock is set when the first vg is seen in which an
|
||||
* enabled gl is exists, or when init_vg creates a vg with gl enabled,
|
||||
* or when enable_gl is used.
|
||||
*
|
||||
* gl_lsname_sanlock is cleared when free_vg deletes a vg with gl enabled
|
||||
* or when disable_gl matches.
|
||||
*/
|
||||
|
||||
EXTERN int gl_running_dlm;
|
||||
EXTERN int gl_type_static;
|
||||
EXTERN int gl_use_dlm;
|
||||
EXTERN int gl_use_sanlock;
|
||||
EXTERN pthread_mutex_t gl_type_mutex;
|
||||
|
||||
EXTERN char gl_lsname_dlm[MAX_NAME+1];
|
||||
EXTERN char gl_lsname_sanlock[MAX_NAME+1];
|
||||
|
||||
EXTERN int daemon_test; /* run as much as possible without a live lock manager */
|
||||
EXTERN int daemon_debug;
|
||||
EXTERN int daemon_host_id;
|
||||
EXTERN const char *daemon_host_id_file;
|
||||
EXTERN int sanlock_io_timeout;
|
||||
|
||||
/*
|
||||
* This flag is set to 1 if we see multiple vgs with the global
|
||||
* lock enabled. While this is set, we return a special flag
|
||||
* with the vg lock result indicating to the lvm command that
|
||||
* there is a duplicate gl in the vg which should be resolved.
|
||||
* While this is set, find_lockspace_name has the side job of
|
||||
* counting the number of lockspaces with enabled gl's so that
|
||||
* this can be set back to zero when the duplicates are disabled.
|
||||
*/
|
||||
EXTERN int sanlock_gl_dup;
|
||||
|
||||
void log_level(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
||||
#define log_debug(fmt, args...) log_level(LOG_DEBUG, fmt, ##args)
|
||||
#define log_error(fmt, args...) log_level(LOG_ERR, fmt, ##args)
|
||||
#define log_warn(fmt, args...) log_level(LOG_WARNING, fmt, ##args)
|
||||
|
||||
struct lockspace *alloc_lockspace(void);
|
||||
int lockspaces_empty(void);
|
||||
int last_string_from_args(char *args_in, char *last);
|
||||
int version_from_args(char *args, unsigned int *major, unsigned int *minor, unsigned int *patch);
|
||||
|
||||
|
||||
#ifdef LOCKDDLM_SUPPORT
|
||||
|
||||
int lm_init_vg_dlm(char *ls_name, char *vg_name, uint32_t flags, char *vg_args);
|
||||
int lm_prepare_lockspace_dlm(struct lockspace *ls);
|
||||
int lm_add_lockspace_dlm(struct lockspace *ls, int adopt);
|
||||
int lm_rem_lockspace_dlm(struct lockspace *ls, int free_vg);
|
||||
int lm_lock_dlm(struct lockspace *ls, struct resource *r, int ld_mode,
|
||||
uint32_t *r_version, int adopt);
|
||||
int lm_convert_dlm(struct lockspace *ls, struct resource *r,
|
||||
int ld_mode, uint32_t r_version);
|
||||
int lm_unlock_dlm(struct lockspace *ls, struct resource *r,
|
||||
uint32_t r_version, uint32_t lmu_flags);
|
||||
int lm_rem_resource_dlm(struct lockspace *ls, struct resource *r);
|
||||
int lm_get_lockspaces_dlm(struct list_head *ls_rejoin);
|
||||
int lm_data_size_dlm(void);
|
||||
int lm_is_running_dlm(void);
|
||||
|
||||
static inline int lm_support_dlm(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int lm_init_vg_dlm(char *ls_name, char *vg_name, uint32_t flags, char *vg_args)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_prepare_lockspace_dlm(struct lockspace *ls)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_add_lockspace_dlm(struct lockspace *ls, int adopt)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_rem_lockspace_dlm(struct lockspace *ls, int free_vg)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_lock_dlm(struct lockspace *ls, struct resource *r, int ld_mode,
|
||||
uint32_t *r_version, int adopt)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_convert_dlm(struct lockspace *ls, struct resource *r,
|
||||
int ld_mode, uint32_t r_version)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_unlock_dlm(struct lockspace *ls, struct resource *r,
|
||||
uint32_t r_version, uint32_t lmu_flags)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_rem_resource_dlm(struct lockspace *ls, struct resource *r)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_get_lockspaces_dlm(struct list_head *ls_rejoin)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_data_size_dlm(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_is_running_dlm(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int lm_support_dlm(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* dlm support */
|
||||
|
||||
#ifdef LOCKDSANLOCK_SUPPORT
|
||||
|
||||
int lm_init_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_args);
|
||||
int lm_init_lv_sanlock(char *ls_name, char *vg_name, char *lv_name, char *vg_args, char *lv_args, uint64_t free_offset);
|
||||
int lm_free_lv_sanlock(struct lockspace *ls, struct resource *r);
|
||||
int lm_rename_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_args);
|
||||
int lm_prepare_lockspace_sanlock(struct lockspace *ls);
|
||||
int lm_add_lockspace_sanlock(struct lockspace *ls, int adopt);
|
||||
int lm_rem_lockspace_sanlock(struct lockspace *ls, int free_vg);
|
||||
int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
|
||||
uint32_t *r_version, int *retry, int adopt);
|
||||
int lm_convert_sanlock(struct lockspace *ls, struct resource *r,
|
||||
int ld_mode, uint32_t r_version);
|
||||
int lm_unlock_sanlock(struct lockspace *ls, struct resource *r,
|
||||
uint32_t r_version, uint32_t lmu_flags);
|
||||
int lm_able_gl_sanlock(struct lockspace *ls, int enable);
|
||||
int lm_ex_disable_gl_sanlock(struct lockspace *ls);
|
||||
int lm_hosts_sanlock(struct lockspace *ls, int notify);
|
||||
int lm_rem_resource_sanlock(struct lockspace *ls, struct resource *r);
|
||||
int lm_gl_is_enabled(struct lockspace *ls);
|
||||
int lm_get_lockspaces_sanlock(struct list_head *ls_rejoin);
|
||||
int lm_data_size_sanlock(void);
|
||||
int lm_is_running_sanlock(void);
|
||||
int lm_find_free_lock_sanlock(struct lockspace *ls, uint64_t *free_offset);
|
||||
|
||||
static inline int lm_support_sanlock(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int lm_init_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_args)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_init_lv_sanlock(char *ls_name, char *vg_name, char *lv_name, char *vg_args, char *lv_args, uint64_t free_offset)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_free_lv_sanlock(struct lockspace *ls, struct resource *r)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_rename_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_args)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_prepare_lockspace_sanlock(struct lockspace *ls)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_add_lockspace_sanlock(struct lockspace *ls, int adopt)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_rem_lockspace_sanlock(struct lockspace *ls, int free_vg)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
|
||||
uint32_t *r_version, int *retry, int adopt)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_convert_sanlock(struct lockspace *ls, struct resource *r,
|
||||
int ld_mode, uint32_t r_version)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_unlock_sanlock(struct lockspace *ls, struct resource *r,
|
||||
uint32_t r_version, uint32_t lmu_flags)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_able_gl_sanlock(struct lockspace *ls, int enable)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_ex_disable_gl_sanlock(struct lockspace *ls)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_hosts_sanlock(struct lockspace *ls, int notify)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_rem_resource_sanlock(struct lockspace *ls, struct resource *r)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_gl_is_enabled(struct lockspace *ls)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_get_lockspaces_sanlock(struct list_head *ls_rejoin)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_data_size_sanlock(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_is_running_sanlock(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int lm_find_free_lock_sanlock(struct lockspace *ls, uint64_t *free_offset)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int lm_support_sanlock(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* sanlock support */
|
||||
|
||||
#endif /* _LVM_LVMLOCKD_INTERNAL_H */
|
||||
1800
daemons/lvmlockd/lvmlockd-sanlock.c
Normal file
1800
daemons/lvmlockd/lvmlockd-sanlock.c
Normal file
File diff suppressed because it is too large
Load Diff
1
daemons/lvmpolld/.gitignore
vendored
Normal file
1
daemons/lvmpolld/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
lvmpolld
|
||||
48
daemons/lvmpolld/Makefile.in
Normal file
48
daemons/lvmpolld/Makefile.in
Normal file
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
#
|
||||
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SOURCES = lvmpolld-core.c lvmpolld-data-utils.c lvmpolld-cmd-utils.c
|
||||
|
||||
TARGETS = lvmpolld
|
||||
|
||||
.PHONY: install_lvmpolld
|
||||
|
||||
CFLOW_LIST = $(SOURCES)
|
||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
||||
CFLOW_TARGET = lvmpolld
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
INCLUDES += -I$(top_srcdir)/libdaemon/server
|
||||
LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper
|
||||
|
||||
LIBS += $(PTHREAD_LIBS)
|
||||
|
||||
LDFLAGS += -L$(top_builddir)/libdaemon/server $(DAEMON_LDFLAGS)
|
||||
CLDFLAGS += -L$(top_builddir)/libdaemon/server
|
||||
CFLAGS += $(DAEMON_CFLAGS)
|
||||
|
||||
lvmpolld: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS)
|
||||
|
||||
install_lvmpolld: lvmpolld
|
||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
||||
|
||||
install_lvm2: install_lvmpolld
|
||||
|
||||
install: install_lvm2
|
||||
144
daemons/lvmpolld/lvmpolld-cmd-utils.c
Normal file
144
daemons/lvmpolld/lvmpolld-cmd-utils.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Red Hat, Inc.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lvmpolld-common.h"
|
||||
|
||||
/* extract this info from autoconf/automake files */
|
||||
#define LVPOLL_CMD "lvpoll"
|
||||
|
||||
#define MIN_ARGV_SIZE 8
|
||||
|
||||
static const char *const const polling_ops[] = { [PVMOVE] = LVMPD_REQ_PVMOVE,
|
||||
[CONVERT] = LVMPD_REQ_CONVERT,
|
||||
[MERGE] = LVMPD_REQ_MERGE,
|
||||
[MERGE_THIN] = LVMPD_REQ_MERGE_THIN };
|
||||
|
||||
const char *polling_op(enum poll_type type)
|
||||
{
|
||||
return type < POLL_TYPE_MAX ? polling_ops[type] : "<undefined>";
|
||||
}
|
||||
|
||||
static int add_to_cmd_arr(const char ***cmdargv, const char *str, unsigned *ind)
|
||||
{
|
||||
const char **newargv = *cmdargv;
|
||||
|
||||
if (*ind && !(*ind % MIN_ARGV_SIZE)) {
|
||||
newargv = dm_realloc(*cmdargv, (*ind / MIN_ARGV_SIZE + 1) * MIN_ARGV_SIZE * sizeof(char *));
|
||||
if (!newargv)
|
||||
return 0;
|
||||
*cmdargv = newargv;
|
||||
}
|
||||
|
||||
*(*cmdargv + (*ind)++) = str;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char **cmdargv_ctr(const struct lvmpolld_lv *pdlv, const char *lvm_binary, unsigned abort_polling, unsigned handle_missing_pvs)
|
||||
{
|
||||
unsigned i = 0;
|
||||
const char **cmd_argv = dm_malloc(MIN_ARGV_SIZE * sizeof(char *));
|
||||
|
||||
if (!cmd_argv)
|
||||
return NULL;
|
||||
|
||||
/* path to lvm2 binary */
|
||||
if (!add_to_cmd_arr(&cmd_argv, lvm_binary, &i))
|
||||
goto err;
|
||||
|
||||
/* cmd to execute */
|
||||
if (!add_to_cmd_arr(&cmd_argv, LVPOLL_CMD, &i))
|
||||
goto err;
|
||||
|
||||
/* transfer internal polling interval */
|
||||
if (pdlv->sinterval &&
|
||||
(!add_to_cmd_arr(&cmd_argv, "--interval", &i) ||
|
||||
!add_to_cmd_arr(&cmd_argv, pdlv->sinterval, &i)))
|
||||
goto err;
|
||||
|
||||
/* pass abort param */
|
||||
if (abort_polling &&
|
||||
!add_to_cmd_arr(&cmd_argv, "--abort", &i))
|
||||
goto err;
|
||||
|
||||
/* pass handle-missing-pvs. used by mirror polling operation */
|
||||
if (handle_missing_pvs &&
|
||||
!add_to_cmd_arr(&cmd_argv, "--handlemissingpvs", &i))
|
||||
goto err;
|
||||
|
||||
/* one of: "convert", "pvmove", "merge", "merge_thin" */
|
||||
if (!add_to_cmd_arr(&cmd_argv, "--polloperation", &i) ||
|
||||
!add_to_cmd_arr(&cmd_argv, polling_ops[pdlv->type], &i))
|
||||
goto err;
|
||||
|
||||
/* vg/lv name */
|
||||
if (!add_to_cmd_arr(&cmd_argv, pdlv->lvname, &i))
|
||||
goto err;
|
||||
|
||||
/* disable metadata backup */
|
||||
if (!add_to_cmd_arr(&cmd_argv, "-An", &i))
|
||||
goto err;
|
||||
|
||||
/* terminating NULL */
|
||||
if (!add_to_cmd_arr(&cmd_argv, NULL, &i))
|
||||
goto err;
|
||||
|
||||
return cmd_argv;
|
||||
err:
|
||||
dm_free(cmd_argv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: in fact exclude should be va list */
|
||||
static int copy_env(const char ***cmd_envp, unsigned *i, const char *exclude)
|
||||
{
|
||||
const char * const* tmp = (const char * const*) environ;
|
||||
|
||||
if (!tmp)
|
||||
return 0;
|
||||
|
||||
while (*tmp) {
|
||||
if (strncmp(*tmp, exclude, strlen(exclude)) && !add_to_cmd_arr(cmd_envp, *tmp, i))
|
||||
return 0;
|
||||
tmp++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char **cmdenvp_ctr(const struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
unsigned i = 0;
|
||||
const char **cmd_envp = dm_malloc(MIN_ARGV_SIZE * sizeof(char *));
|
||||
|
||||
if (!cmd_envp)
|
||||
return NULL;
|
||||
|
||||
/* copy whole environment from lvmpolld, exclude LVM_SYSTEM_DIR if set */
|
||||
if (!copy_env(&cmd_envp, &i, "LVM_SYSTEM_DIR="))
|
||||
goto err;
|
||||
|
||||
/* Add per client LVM_SYSTEM_DIR variable if set */
|
||||
if (*pdlv->lvm_system_dir_env && !add_to_cmd_arr(&cmd_envp, pdlv->lvm_system_dir_env, &i))
|
||||
goto err;
|
||||
|
||||
/* terminating NULL */
|
||||
if (!add_to_cmd_arr(&cmd_envp, NULL, &i))
|
||||
goto err;
|
||||
|
||||
return cmd_envp;
|
||||
err:
|
||||
dm_free(cmd_envp);
|
||||
return NULL;
|
||||
}
|
||||
25
daemons/lvmpolld/lvmpolld-cmd-utils.h
Normal file
25
daemons/lvmpolld/lvmpolld-cmd-utils.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Red Hat, Inc.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LVMPOLLD_CMD_UTILS_H
|
||||
#define _LVM_LVMPOLLD_CMD_UTILS_H
|
||||
|
||||
#include "lvmpolld-data-utils.h"
|
||||
|
||||
const char **cmdargv_ctr(const struct lvmpolld_lv *pdlv, const char *lvm_binary, unsigned abort, unsigned handle_missing_pvs);
|
||||
const char **cmdenvp_ctr(const struct lvmpolld_lv *pdlv);
|
||||
|
||||
const char *polling_op(enum poll_type);
|
||||
|
||||
#endif /* _LVM_LVMPOLLD_CMD_UTILS_H */
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Rackable Systems All rights reserved.
|
||||
* Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -12,22 +12,20 @@
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_TIMESTAMP_H
|
||||
#define _LVM_TIMESTAMP_H
|
||||
|
||||
struct timestamp;
|
||||
|
||||
struct timestamp *get_timestamp(void);
|
||||
|
||||
/* cmp_timestamp: Compare two timestamps
|
||||
*
|
||||
* Return: -1 if t1 is less than t2
|
||||
* 0 if t1 is equal to t2
|
||||
* 1 if t1 is greater than t2
|
||||
/*
|
||||
* This file must be included first by every lvmpolld source file.
|
||||
*/
|
||||
int cmp_timestamp(struct timestamp *t1, struct timestamp *t2);
|
||||
#ifndef _LVM_LVMPOLLD_COMMON_H
|
||||
#define _LVM_LVMPOLLD_COMMON_H
|
||||
|
||||
void destroy_timestamp(struct timestamp *t);
|
||||
#define _REENTRANT
|
||||
|
||||
#endif /* _LVM_TIMESTAMP_H */
|
||||
#include "tool.h"
|
||||
|
||||
#include "lvmpolld-cmd-utils.h"
|
||||
#include "lvmpolld-protocol.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#endif /* _LVM_LVMPOLLD_COMMON_H */
|
||||
985
daemons/lvmpolld/lvmpolld-core.c
Normal file
985
daemons/lvmpolld/lvmpolld-core.c
Normal file
@@ -0,0 +1,985 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lvmpolld-common.h"
|
||||
|
||||
#include "lvm-version.h"
|
||||
#include "daemon-server.h"
|
||||
#include "daemon-log.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <poll.h>
|
||||
#include <wait.h>
|
||||
|
||||
#define LVMPOLLD_SOCKET DEFAULT_RUN_DIR "/lvmpolld.socket"
|
||||
|
||||
#define PD_LOG_PREFIX "LVMPOLLD"
|
||||
#define LVM2_LOG_PREFIX "\tLVPOLL"
|
||||
|
||||
/* predefined reason for response = "failed" case */
|
||||
#define REASON_REQ_NOT_IMPLEMENTED "request not implemented"
|
||||
#define REASON_MISSING_LVID "request requires lvid set"
|
||||
#define REASON_MISSING_LVNAME "request requires lvname set"
|
||||
#define REASON_MISSING_VGNAME "request requires vgname set"
|
||||
#define REASON_POLLING_FAILED "polling of lvm command failed"
|
||||
#define REASON_ILLEGAL_ABORT_REQUEST "abort only supported with PVMOVE polling operation"
|
||||
#define REASON_DIFFERENT_OPERATION_IN_PROGRESS "Different operation on LV already in progress"
|
||||
#define REASON_INVALID_INTERVAL "request requires interval set"
|
||||
#define REASON_ENOMEM "not enough memory"
|
||||
|
||||
struct lvmpolld_state {
|
||||
daemon_idle *idle;
|
||||
log_state *log;
|
||||
const char *log_config;
|
||||
const char *lvm_binary;
|
||||
|
||||
struct lvmpolld_store *id_to_pdlv_abort;
|
||||
struct lvmpolld_store *id_to_pdlv_poll;
|
||||
};
|
||||
|
||||
static pthread_key_t key;
|
||||
|
||||
static const char *_strerror_r(int errnum, struct lvmpolld_thread_data *data)
|
||||
{
|
||||
#ifdef _GNU_SOURCE
|
||||
return strerror_r(errnum, data->buf, sizeof(data->buf)); /* never returns NULL */
|
||||
#elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
|
||||
return strerror_r(errnum, data->buf, sizeof(data->buf)) ? "" : data->buf;
|
||||
#else
|
||||
# warning "Can't decide proper strerror_r implementation. lvmpolld will not issue specific system error messages"
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _usage(const char *prog, FILE *file)
|
||||
{
|
||||
fprintf(file, "Usage:\n"
|
||||
"%s [-V] [-h] [-f] [-l {all|wire|debug}] [-s path] [-B path] [-p path] [-t secs]\n"
|
||||
"%s --dump [-s path]\n"
|
||||
" -V|--version Show version info\n"
|
||||
" -h|--help Show this help information\n"
|
||||
" -f|--foreground Don't fork, run in the foreground\n"
|
||||
" --dump Dump full lvmpolld state\n"
|
||||
" -l|--log Logging message level (-l {all|wire|debug})\n"
|
||||
" -p|--pidfile Set path to the pidfile\n"
|
||||
" -s|--socket Set path to the communication socket\n"
|
||||
" -B|--binary Path to lvm2 binary\n"
|
||||
" -t|--timeout Time to wait in seconds before shutdown on idle (missing or 0 = inifinite)\n\n", prog, prog);
|
||||
}
|
||||
|
||||
static int _init(struct daemon_state *s)
|
||||
{
|
||||
struct lvmpolld_state *ls = s->private;
|
||||
ls->log = s->log;
|
||||
|
||||
if (!daemon_log_parse(ls->log, DAEMON_LOG_OUTLET_STDERR, ls->log_config, 1))
|
||||
return 0;
|
||||
|
||||
if (pthread_key_create(&key, lvmpolld_thread_data_destroy)) {
|
||||
FATAL(ls, "%s: %s", PD_LOG_PREFIX, "Failed to create pthread key");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ls->id_to_pdlv_poll = pdst_init("polling");
|
||||
ls->id_to_pdlv_abort = pdst_init("abort");
|
||||
|
||||
if (!ls->id_to_pdlv_poll || !ls->id_to_pdlv_abort) {
|
||||
FATAL(ls, "%s: %s", PD_LOG_PREFIX, "Failed to allocate internal data structures");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ls->lvm_binary = ls->lvm_binary ?: LVM_PATH;
|
||||
|
||||
if (access(ls->lvm_binary, X_OK)) {
|
||||
FATAL(ls, "%s: %s %s", PD_LOG_PREFIX, "Execute access rights denied on", ls->lvm_binary);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ls->idle)
|
||||
ls->idle->is_idle = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _lvmpolld_stores_lock(struct lvmpolld_state *ls)
|
||||
{
|
||||
pdst_lock(ls->id_to_pdlv_poll);
|
||||
pdst_lock(ls->id_to_pdlv_abort);
|
||||
}
|
||||
|
||||
static void _lvmpolld_stores_unlock(struct lvmpolld_state *ls)
|
||||
{
|
||||
pdst_unlock(ls->id_to_pdlv_abort);
|
||||
pdst_unlock(ls->id_to_pdlv_poll);
|
||||
}
|
||||
|
||||
static void _lvmpolld_global_lock(struct lvmpolld_state *ls)
|
||||
{
|
||||
_lvmpolld_stores_lock(ls);
|
||||
|
||||
pdst_locked_lock_all_pdlvs(ls->id_to_pdlv_poll);
|
||||
pdst_locked_lock_all_pdlvs(ls->id_to_pdlv_abort);
|
||||
}
|
||||
|
||||
static void _lvmpolld_global_unlock(struct lvmpolld_state *ls)
|
||||
{
|
||||
pdst_locked_unlock_all_pdlvs(ls->id_to_pdlv_abort);
|
||||
pdst_locked_unlock_all_pdlvs(ls->id_to_pdlv_poll);
|
||||
|
||||
_lvmpolld_stores_unlock(ls);
|
||||
}
|
||||
|
||||
static int _fini(struct daemon_state *s)
|
||||
{
|
||||
int done;
|
||||
const struct timespec t = { .tv_nsec = 250000000 }; /* .25 sec */
|
||||
struct lvmpolld_state *ls = s->private;
|
||||
|
||||
DEBUGLOG(s, "fini");
|
||||
|
||||
DEBUGLOG(s, "sending cancel requests");
|
||||
|
||||
_lvmpolld_global_lock(ls);
|
||||
pdst_locked_send_cancel(ls->id_to_pdlv_poll);
|
||||
pdst_locked_send_cancel(ls->id_to_pdlv_abort);
|
||||
_lvmpolld_global_unlock(ls);
|
||||
|
||||
DEBUGLOG(s, "waiting for background threads to finish");
|
||||
|
||||
while(1) {
|
||||
_lvmpolld_stores_lock(ls);
|
||||
done = !pdst_locked_get_active_count(ls->id_to_pdlv_poll) &&
|
||||
!pdst_locked_get_active_count(ls->id_to_pdlv_abort);
|
||||
_lvmpolld_stores_unlock(ls);
|
||||
if (done)
|
||||
break;
|
||||
nanosleep(&t, NULL);
|
||||
}
|
||||
|
||||
DEBUGLOG(s, "destroying internal data structures");
|
||||
|
||||
_lvmpolld_stores_lock(ls);
|
||||
pdst_locked_destroy_all_pdlvs(ls->id_to_pdlv_poll);
|
||||
pdst_locked_destroy_all_pdlvs(ls->id_to_pdlv_abort);
|
||||
_lvmpolld_stores_unlock(ls);
|
||||
|
||||
pdst_destroy(ls->id_to_pdlv_poll);
|
||||
pdst_destroy(ls->id_to_pdlv_abort);
|
||||
|
||||
pthread_key_delete(key);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static response reply(const char *res, const char *reason)
|
||||
{
|
||||
return daemon_reply_simple(res, "reason = %s", reason, NULL);
|
||||
}
|
||||
|
||||
static int read_single_line(struct lvmpolld_thread_data *data, int err)
|
||||
{
|
||||
ssize_t r = getline(&data->line, &data->line_size, err ? data->ferr : data->fout);
|
||||
|
||||
if (r > 0 && *(data->line + r - 1) == '\n')
|
||||
*(data->line + r - 1) = '\0';
|
||||
|
||||
return (r > 0);
|
||||
}
|
||||
|
||||
static void update_idle_state(struct lvmpolld_state *ls)
|
||||
{
|
||||
if (!ls->idle)
|
||||
return;
|
||||
|
||||
_lvmpolld_stores_lock(ls);
|
||||
|
||||
ls->idle->is_idle = !pdst_locked_get_active_count(ls->id_to_pdlv_poll) &&
|
||||
!pdst_locked_get_active_count(ls->id_to_pdlv_abort);
|
||||
|
||||
_lvmpolld_stores_unlock(ls);
|
||||
|
||||
DEBUGLOG(ls, "%s: %s %s%s", PD_LOG_PREFIX, "daemon is", ls->idle->is_idle ? "" : "not ", "idle");
|
||||
}
|
||||
|
||||
/* make this configurable */
|
||||
#define MAX_TIMEOUT 2
|
||||
|
||||
static int poll_for_output(struct lvmpolld_lv *pdlv, struct lvmpolld_thread_data *data)
|
||||
{
|
||||
int ch_stat, r, err = 1, fds_count = 2, timeout = 0;
|
||||
pid_t pid;
|
||||
struct lvmpolld_cmd_stat cmd_state = { .retcode = -1, .signal = 0 };
|
||||
struct pollfd fds[] = { { .fd = data->outpipe[0], .events = POLLIN },
|
||||
{ .fd = data->errpipe[0], .events = POLLIN } };
|
||||
|
||||
if (!(data->fout = fdopen(data->outpipe[0], "r")) || !(data->ferr = fdopen(data->errpipe[0], "r"))) {
|
||||
ERROR(pdlv->ls, "%s: %s: (%d) %s", PD_LOG_PREFIX, "failed to open file stream",
|
||||
errno, _strerror_r(errno, data));
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
do {
|
||||
r = poll(fds, 2, pdlv_get_timeout(pdlv) * 1000);
|
||||
} while (r < 0 && errno == EINTR);
|
||||
|
||||
DEBUGLOG(pdlv->ls, "%s: %s %d", PD_LOG_PREFIX, "poll() returned", r);
|
||||
if (r < 0) {
|
||||
ERROR(pdlv->ls, "%s: %s (PID %d) failed: (%d) %s",
|
||||
PD_LOG_PREFIX, "poll() for LVM2 cmd", pdlv->cmd_pid,
|
||||
errno, _strerror_r(errno, data));
|
||||
goto out;
|
||||
} else if (!r) {
|
||||
timeout++;
|
||||
|
||||
WARN(pdlv->ls, "%s: %s (PID %d) %s", PD_LOG_PREFIX,
|
||||
"polling for output of the lvm cmd", pdlv->cmd_pid,
|
||||
"has timed out");
|
||||
|
||||
if (timeout > MAX_TIMEOUT) {
|
||||
ERROR(pdlv->ls, "%s: %s (PID %d) (no output for %d seconds)",
|
||||
PD_LOG_PREFIX,
|
||||
"LVM2 cmd is unresponsive too long",
|
||||
pdlv->cmd_pid,
|
||||
timeout * pdlv_get_timeout(pdlv));
|
||||
goto out;
|
||||
}
|
||||
|
||||
continue; /* while(1) */
|
||||
}
|
||||
|
||||
timeout = 0;
|
||||
|
||||
/* handle the command's STDOUT */
|
||||
if (fds[0].revents & POLLIN) {
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "caught input data in STDOUT");
|
||||
|
||||
assert(read_single_line(data, 0)); /* may block indef. anyway */
|
||||
INFO(pdlv->ls, "%s: PID %d: %s: '%s'", LVM2_LOG_PREFIX,
|
||||
pdlv->cmd_pid, "STDOUT", data->line);
|
||||
} else if (fds[0].revents) {
|
||||
if (fds[0].revents & POLLHUP)
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "caught POLLHUP");
|
||||
else
|
||||
WARN(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "poll for command's STDOUT failed");
|
||||
|
||||
fds[0].fd = -1;
|
||||
fds_count--;
|
||||
}
|
||||
|
||||
/* handle the command's STDERR */
|
||||
if (fds[1].revents & POLLIN) {
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX,
|
||||
"caught input data in STDERR");
|
||||
|
||||
assert(read_single_line(data, 1)); /* may block indef. anyway */
|
||||
INFO(pdlv->ls, "%s: PID %d: %s: '%s'", LVM2_LOG_PREFIX,
|
||||
pdlv->cmd_pid, "STDERR", data->line);
|
||||
} else if (fds[1].revents) {
|
||||
if (fds[1].revents & POLLHUP)
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "caught err POLLHUP");
|
||||
else
|
||||
WARN(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "poll for command's STDOUT failed");
|
||||
|
||||
fds[1].fd = -1;
|
||||
fds_count--;
|
||||
}
|
||||
|
||||
do {
|
||||
/*
|
||||
* fds_count == 0 means polling reached EOF
|
||||
* or received error on both descriptors.
|
||||
* In such case, just wait for command to finish
|
||||
*/
|
||||
pid = waitpid(pdlv->cmd_pid, &ch_stat, fds_count ? WNOHANG : 0);
|
||||
} while (pid < 0 && errno == EINTR);
|
||||
|
||||
if (pid) {
|
||||
if (pid < 0) {
|
||||
ERROR(pdlv->ls, "%s: %s (PID %d) failed: (%d) %s",
|
||||
PD_LOG_PREFIX, "waitpid() for lvm2 cmd",
|
||||
pdlv->cmd_pid, errno,
|
||||
_strerror_r(errno, data));
|
||||
goto out;
|
||||
}
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "child exited");
|
||||
break;
|
||||
}
|
||||
} /* while(1) */
|
||||
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "about to collect remaining lines");
|
||||
if (fds[0].fd >= 0)
|
||||
while (read_single_line(data, 0)) {
|
||||
assert(r > 0);
|
||||
INFO(pdlv->ls, "%s: PID %d: %s: %s", LVM2_LOG_PREFIX, pdlv->cmd_pid, "STDOUT", data->line);
|
||||
}
|
||||
if (fds[1].fd >= 0)
|
||||
while (read_single_line(data, 1)) {
|
||||
assert(r > 0);
|
||||
INFO(pdlv->ls, "%s: PID %d: %s: %s", LVM2_LOG_PREFIX, pdlv->cmd_pid, "STDERR", data->line);
|
||||
}
|
||||
|
||||
if (WIFEXITED(ch_stat)) {
|
||||
INFO(pdlv->ls, "%s: %s (PID %d) %s (%d)", PD_LOG_PREFIX,
|
||||
"lvm2 cmd", pdlv->cmd_pid, "exited with", WEXITSTATUS(ch_stat));
|
||||
cmd_state.retcode = WEXITSTATUS(ch_stat);
|
||||
} else if (WIFSIGNALED(ch_stat)) {
|
||||
WARN(pdlv->ls, "%s: %s (PID %d) %s (%d)", PD_LOG_PREFIX,
|
||||
"lvm2 cmd", pdlv->cmd_pid, "got terminated by signal",
|
||||
WTERMSIG(ch_stat));
|
||||
cmd_state.signal = WTERMSIG(ch_stat);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
if (!err)
|
||||
pdlv_set_cmd_state(pdlv, &cmd_state);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void debug_print(struct lvmpolld_state *ls, const char * const* ptr)
|
||||
{
|
||||
const char * const* tmp = ptr;
|
||||
|
||||
if (!tmp)
|
||||
return;
|
||||
|
||||
while (*tmp) {
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, *tmp);
|
||||
tmp++;
|
||||
}
|
||||
}
|
||||
|
||||
static void *fork_and_poll(void *args)
|
||||
{
|
||||
int outfd, errfd, state;
|
||||
struct lvmpolld_thread_data *data;
|
||||
pid_t r;
|
||||
|
||||
int error = 1;
|
||||
struct lvmpolld_lv *pdlv = (struct lvmpolld_lv *) args;
|
||||
struct lvmpolld_state *ls = pdlv->ls;
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
|
||||
data = lvmpolld_thread_data_constructor(pdlv);
|
||||
pthread_setspecific(key, data);
|
||||
pthread_setcancelstate(state, &state);
|
||||
|
||||
if (!data) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "Failed to initialize per-thread data");
|
||||
goto err;
|
||||
}
|
||||
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "cmd line arguments:");
|
||||
debug_print(ls, pdlv->cmdargv);
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "---end---");
|
||||
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "cmd environment variables:");
|
||||
debug_print(ls, pdlv->cmdenvp);
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "---end---");
|
||||
|
||||
outfd = data->outpipe[1];
|
||||
errfd = data->errpipe[1];
|
||||
|
||||
r = fork();
|
||||
if (!r) {
|
||||
/* child */
|
||||
/* !!! Do not touch any posix thread primitives !!! */
|
||||
|
||||
if ((dup2(outfd, STDOUT_FILENO ) != STDOUT_FILENO) ||
|
||||
(dup2(errfd, STDERR_FILENO ) != STDERR_FILENO))
|
||||
_exit(LVMPD_RET_DUP_FAILED);
|
||||
|
||||
execve(*(pdlv->cmdargv), (char *const *)pdlv->cmdargv, (char *const *)pdlv->cmdenvp);
|
||||
|
||||
_exit(LVMPD_RET_EXC_FAILED);
|
||||
} else {
|
||||
/* parent */
|
||||
if (r == -1) {
|
||||
ERROR(ls, "%s: %s: (%d) %s", PD_LOG_PREFIX, "fork failed",
|
||||
errno, _strerror_r(errno, data));
|
||||
goto err;
|
||||
}
|
||||
|
||||
INFO(ls, "%s: LVM2 cmd \"%s\" (PID: %d)", PD_LOG_PREFIX, *(pdlv->cmdargv), r);
|
||||
|
||||
pdlv->cmd_pid = r;
|
||||
|
||||
/* failure to close write end of any pipe will result in broken polling */
|
||||
if (close(data->outpipe[1])) {
|
||||
ERROR(ls, "%s: %s: (%d) %s", PD_LOG_PREFIX, "failed to close write end of pipe",
|
||||
errno, _strerror_r(errno, data));
|
||||
goto err;
|
||||
}
|
||||
data->outpipe[1] = -1;
|
||||
|
||||
if (close(data->errpipe[1])) {
|
||||
ERROR(ls, "%s: %s: (%d) %s", PD_LOG_PREFIX, "failed to close write end of err pipe",
|
||||
errno, _strerror_r(errno, data));
|
||||
goto err;
|
||||
}
|
||||
data->errpipe[1] = -1;
|
||||
|
||||
error = poll_for_output(pdlv, data);
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "polling for lvpoll output has finished");
|
||||
}
|
||||
|
||||
err:
|
||||
r = 0;
|
||||
|
||||
pdst_lock(pdlv->pdst);
|
||||
|
||||
if (error) {
|
||||
/* last reader is responsible for pdlv cleanup */
|
||||
r = pdlv->cmd_pid;
|
||||
pdlv_set_error(pdlv, 1);
|
||||
}
|
||||
|
||||
pdlv_set_polling_finished(pdlv, 1);
|
||||
if (data)
|
||||
data->pdlv = NULL;
|
||||
|
||||
pdst_locked_dec(pdlv->pdst);
|
||||
|
||||
pdst_unlock(pdlv->pdst);
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
|
||||
lvmpolld_thread_data_destroy(data);
|
||||
pthread_setspecific(key, NULL);
|
||||
pthread_setcancelstate(state, &state);
|
||||
|
||||
update_idle_state(ls);
|
||||
|
||||
/*
|
||||
* This is unfortunate case where we
|
||||
* know nothing about state of lvm cmd and
|
||||
* (eventually) ongoing progress.
|
||||
*
|
||||
* harvest zombies
|
||||
*/
|
||||
if (r)
|
||||
while(waitpid(r, NULL, 0) < 0 && errno == EINTR);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static response progress_info(client_handle h, struct lvmpolld_state *ls, request req)
|
||||
{
|
||||
char *id;
|
||||
struct lvmpolld_lv *pdlv;
|
||||
struct lvmpolld_store *pdst;
|
||||
struct lvmpolld_lv_state st;
|
||||
response r;
|
||||
const char *lvid = daemon_request_str(req, LVMPD_PARM_LVID, NULL);
|
||||
const char *sysdir = daemon_request_str(req, LVMPD_PARM_SYSDIR, NULL);
|
||||
unsigned abort_polling = daemon_request_int(req, LVMPD_PARM_ABORT, 0);
|
||||
|
||||
if (!lvid)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_MISSING_LVID);
|
||||
|
||||
id = construct_id(sysdir, lvid);
|
||||
if (!id) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "progress_info request failed to construct ID.");
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
|
||||
DEBUGLOG(ls, "%s: %s: %s", PD_LOG_PREFIX, "ID", id);
|
||||
|
||||
pdst = abort_polling ? ls->id_to_pdlv_abort : ls->id_to_pdlv_poll;
|
||||
|
||||
pdst_lock(pdst);
|
||||
|
||||
pdlv = pdst_locked_lookup(pdst, id);
|
||||
if (pdlv) {
|
||||
/*
|
||||
* with store lock held, I'm the only reader accessing the pdlv
|
||||
*/
|
||||
st = pdlv_get_status(pdlv);
|
||||
|
||||
if (st.error || st.polling_finished) {
|
||||
INFO(ls, "%s: %s %s", PD_LOG_PREFIX,
|
||||
"Polling finished. Removing related data structure for LV",
|
||||
lvid);
|
||||
pdst_locked_remove(pdst, id);
|
||||
pdlv_destroy(pdlv);
|
||||
}
|
||||
}
|
||||
/* pdlv must not be dereferenced from now on */
|
||||
|
||||
pdst_unlock(pdst);
|
||||
|
||||
dm_free(id);
|
||||
|
||||
if (pdlv) {
|
||||
if (st.error)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_POLLING_FAILED);
|
||||
|
||||
if (st.polling_finished)
|
||||
r = daemon_reply_simple(LVMPD_RESP_FINISHED,
|
||||
"reason = %s", st.cmd_state.signal ? LVMPD_REAS_SIGNAL : LVMPD_REAS_RETCODE,
|
||||
LVMPD_PARM_VALUE " = %d", (int64_t)(st.cmd_state.signal ?: st.cmd_state.retcode),
|
||||
NULL);
|
||||
else
|
||||
r = daemon_reply_simple(LVMPD_RESP_IN_PROGRESS, NULL);
|
||||
}
|
||||
else
|
||||
r = daemon_reply_simple(LVMPD_RESP_NOT_FOUND, NULL);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct lvmpolld_lv *construct_pdlv(request req, struct lvmpolld_state *ls,
|
||||
struct lvmpolld_store *pdst,
|
||||
const char *interval, const char *id,
|
||||
const char *vgname, const char *lvname,
|
||||
const char *sysdir, enum poll_type type,
|
||||
unsigned abort_polling, unsigned uinterval)
|
||||
{
|
||||
const char **cmdargv, **cmdenvp;
|
||||
struct lvmpolld_lv *pdlv;
|
||||
unsigned handle_missing_pvs = daemon_request_int(req, LVMPD_PARM_HANDLE_MISSING_PVS, 0);
|
||||
|
||||
pdlv = pdlv_create(ls, id, vgname, lvname, sysdir, type,
|
||||
interval, uinterval, pdst);
|
||||
|
||||
if (!pdlv) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "failed to create internal LV data structure.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmdargv = cmdargv_ctr(pdlv, pdlv->ls->lvm_binary, abort_polling, handle_missing_pvs);
|
||||
if (!cmdargv) {
|
||||
pdlv_destroy(pdlv);
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "failed to construct cmd arguments for lvpoll command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pdlv->cmdargv = cmdargv;
|
||||
|
||||
cmdenvp = cmdenvp_ctr(pdlv);
|
||||
if (!cmdenvp) {
|
||||
pdlv_destroy(pdlv);
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "failed to construct cmd environment for lvpoll command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pdlv->cmdenvp = cmdenvp;
|
||||
|
||||
return pdlv;
|
||||
}
|
||||
|
||||
static int spawn_detached_thread(struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
int r;
|
||||
pthread_attr_t attr;
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
r = pthread_create(&pdlv->tid, &attr, fork_and_poll, (void *)pdlv);
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
return !r;
|
||||
}
|
||||
|
||||
static response poll_init(client_handle h, struct lvmpolld_state *ls, request req, enum poll_type type)
|
||||
{
|
||||
char *id;
|
||||
struct lvmpolld_lv *pdlv;
|
||||
struct lvmpolld_store *pdst;
|
||||
unsigned uinterval;
|
||||
|
||||
const char *interval = daemon_request_str(req, LVMPD_PARM_INTERVAL, NULL);
|
||||
const char *lvid = daemon_request_str(req, LVMPD_PARM_LVID, NULL);
|
||||
const char *lvname = daemon_request_str(req, LVMPD_PARM_LVNAME, NULL);
|
||||
const char *vgname = daemon_request_str(req, LVMPD_PARM_VGNAME, NULL);
|
||||
const char *sysdir = daemon_request_str(req, LVMPD_PARM_SYSDIR, NULL);
|
||||
unsigned abort_polling = daemon_request_int(req, LVMPD_PARM_ABORT, 0);
|
||||
|
||||
assert(type < POLL_TYPE_MAX);
|
||||
|
||||
if (abort_polling && type != PVMOVE)
|
||||
return reply(LVMPD_RESP_EINVAL, REASON_ILLEGAL_ABORT_REQUEST);
|
||||
|
||||
if (!interval || strpbrk(interval, "-") || sscanf(interval, "%u", &uinterval) != 1)
|
||||
return reply(LVMPD_RESP_EINVAL, REASON_INVALID_INTERVAL);
|
||||
|
||||
if (!lvname)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_MISSING_LVNAME);
|
||||
|
||||
if (!lvid)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_MISSING_LVID);
|
||||
|
||||
if (!vgname)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_MISSING_VGNAME);
|
||||
|
||||
id = construct_id(sysdir, lvid);
|
||||
if (!id) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "poll_init request failed to construct ID.");
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
|
||||
DEBUGLOG(ls, "%s: %s=%s", PD_LOG_PREFIX, "ID", id);
|
||||
|
||||
pdst = abort_polling ? ls->id_to_pdlv_abort : ls->id_to_pdlv_poll;
|
||||
|
||||
pdst_lock(pdst);
|
||||
|
||||
pdlv = pdst_locked_lookup(pdst, id);
|
||||
if (pdlv && pdlv_get_polling_finished(pdlv)) {
|
||||
WARN(ls, "%s: %s %s", PD_LOG_PREFIX, "Force removal of uncollected info for LV",
|
||||
lvid);
|
||||
/*
|
||||
* lvmpolld has to remove uncollected results in this case.
|
||||
* otherwise it would have to refuse request for new polling
|
||||
* lv with same id.
|
||||
*/
|
||||
pdst_locked_remove(pdst, id);
|
||||
pdlv_destroy(pdlv);
|
||||
pdlv = NULL;
|
||||
}
|
||||
|
||||
if (pdlv) {
|
||||
if (!pdlv_is_type(pdlv, type)) {
|
||||
pdst_unlock(pdst);
|
||||
ERROR(ls, "%s: %s '%s': expected: %s, requested: %s",
|
||||
PD_LOG_PREFIX, "poll operation type mismatch on LV identified by",
|
||||
id,
|
||||
polling_op(pdlv_get_type(pdlv)), polling_op(type));
|
||||
dm_free(id);
|
||||
return reply(LVMPD_RESP_EINVAL,
|
||||
REASON_DIFFERENT_OPERATION_IN_PROGRESS);
|
||||
}
|
||||
pdlv->init_rq_count++; /* safe. protected by store lock */
|
||||
} else {
|
||||
pdlv = construct_pdlv(req, ls, pdst, interval, id, vgname,
|
||||
lvname, sysdir, type, abort_polling, 2 * uinterval);
|
||||
if (!pdlv) {
|
||||
pdst_unlock(pdst);
|
||||
dm_free(id);
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
if (!pdst_locked_insert(pdst, id, pdlv)) {
|
||||
pdlv_destroy(pdlv);
|
||||
pdst_unlock(pdst);
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "couldn't store internal LV data structure");
|
||||
dm_free(id);
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
if (!spawn_detached_thread(pdlv)) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "failed to spawn detached monitoring thread");
|
||||
pdst_locked_remove(pdst, id);
|
||||
pdlv_destroy(pdlv);
|
||||
pdst_unlock(pdst);
|
||||
dm_free(id);
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
|
||||
pdst_locked_inc(pdst);
|
||||
if (ls->idle)
|
||||
ls->idle->is_idle = 0;
|
||||
}
|
||||
|
||||
pdst_unlock(pdst);
|
||||
|
||||
dm_free(id);
|
||||
|
||||
return daemon_reply_simple(LVMPD_RESP_OK, NULL);
|
||||
}
|
||||
|
||||
static response dump_state(client_handle h, struct lvmpolld_state *ls, request r)
|
||||
{
|
||||
response res = { 0 };
|
||||
struct buffer *b = &res.buffer;
|
||||
|
||||
buffer_init(b);
|
||||
|
||||
_lvmpolld_global_lock(ls);
|
||||
|
||||
buffer_append(b, "# Registered polling operations\n\n");
|
||||
buffer_append(b, "poll {\n");
|
||||
pdst_locked_dump(ls->id_to_pdlv_poll, b);
|
||||
buffer_append(b, "}\n\n");
|
||||
|
||||
buffer_append(b, "# Registered abort operations\n\n");
|
||||
buffer_append(b, "abort {\n");
|
||||
pdst_locked_dump(ls->id_to_pdlv_abort, b);
|
||||
buffer_append(b, "}");
|
||||
|
||||
_lvmpolld_global_unlock(ls);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static response _handler(struct daemon_state s, client_handle h, request r)
|
||||
{
|
||||
struct lvmpolld_state *ls = s.private;
|
||||
const char *rq = daemon_request_str(r, "request", "NONE");
|
||||
|
||||
if (!strcmp(rq, LVMPD_REQ_PVMOVE))
|
||||
return poll_init(h, ls, r, PVMOVE);
|
||||
else if (!strcmp(rq, LVMPD_REQ_CONVERT))
|
||||
return poll_init(h, ls, r, CONVERT);
|
||||
else if (!strcmp(rq, LVMPD_REQ_MERGE))
|
||||
return poll_init(h, ls, r, MERGE);
|
||||
else if (!strcmp(rq, LVMPD_REQ_MERGE_THIN))
|
||||
return poll_init(h, ls, r, MERGE_THIN);
|
||||
else if (!strcmp(rq, LVMPD_REQ_PROGRESS))
|
||||
return progress_info(h, ls, r);
|
||||
else if (!strcmp(rq, LVMPD_REQ_DUMP))
|
||||
return dump_state(h, ls, r);
|
||||
else
|
||||
return reply(LVMPD_RESP_EINVAL, REASON_REQ_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
static int process_timeout_arg(const char *str, unsigned *max_timeouts)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long l;
|
||||
|
||||
errno = 0;
|
||||
l = strtoul(str, &endptr, 10);
|
||||
if (errno || *endptr || l >= UINT_MAX)
|
||||
return 0;
|
||||
|
||||
*max_timeouts = (unsigned) l;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Client functionality */
|
||||
typedef int (*action_fn_t) (void *args);
|
||||
|
||||
struct log_line_baton {
|
||||
const char *prefix;
|
||||
};
|
||||
|
||||
daemon_handle _lvmpolld = { .error = 0 };
|
||||
|
||||
static daemon_handle _lvmpolld_open(const char *socket)
|
||||
{
|
||||
daemon_info lvmpolld_info = {
|
||||
.path = "lvmpolld",
|
||||
.socket = socket ?: DEFAULT_RUN_DIR "/lvmpolld.socket",
|
||||
.protocol = LVMPOLLD_PROTOCOL,
|
||||
.protocol_version = LVMPOLLD_PROTOCOL_VERSION
|
||||
};
|
||||
|
||||
return daemon_open(lvmpolld_info);
|
||||
}
|
||||
|
||||
static void _log_line(const char *line, void *baton) {
|
||||
struct log_line_baton *b = baton;
|
||||
fprintf(stdout, "%s%s\n", b->prefix, line);
|
||||
}
|
||||
|
||||
static int printout_raw_response(const char *prefix, const char *msg)
|
||||
{
|
||||
struct log_line_baton b = { .prefix = prefix };
|
||||
char *buf;
|
||||
char *pos;
|
||||
|
||||
buf = dm_strdup(msg);
|
||||
pos = buf;
|
||||
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
while (pos) {
|
||||
char *next = strchr(pos, '\n');
|
||||
if (next)
|
||||
*next = 0;
|
||||
_log_line(pos, &b);
|
||||
pos = next ? next + 1 : 0;
|
||||
}
|
||||
dm_free(buf);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* place all action implementations below */
|
||||
|
||||
static int action_dump(void *args __attribute__((unused)))
|
||||
{
|
||||
daemon_request req;
|
||||
daemon_reply repl;
|
||||
int r = 0;
|
||||
|
||||
req = daemon_request_make(LVMPD_REQ_DUMP);
|
||||
if (!req.cft) {
|
||||
fprintf(stderr, "Failed to create lvmpolld " LVMPD_REQ_DUMP " request.\n");
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
repl = daemon_send(_lvmpolld, req);
|
||||
if (repl.error) {
|
||||
fprintf(stderr, "Failed to send a request or receive response.\n");
|
||||
goto out_rep;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is dumb copy & paste from libdaemon log routines.
|
||||
*/
|
||||
if (!printout_raw_response(" ", repl.buffer.mem)) {
|
||||
fprintf(stderr, "Failed to print out the response.\n");
|
||||
goto out_rep;
|
||||
}
|
||||
|
||||
r = 1;
|
||||
|
||||
out_rep:
|
||||
daemon_reply_destroy(repl);
|
||||
out_req:
|
||||
daemon_request_destroy(req);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
enum action_index {
|
||||
ACTION_DUMP = 0,
|
||||
ACTION_MAX /* keep at the end */
|
||||
};
|
||||
|
||||
static const action_fn_t actions[ACTION_MAX] = { [ACTION_DUMP] = action_dump };
|
||||
|
||||
static int _make_action(enum action_index idx, void *args)
|
||||
{
|
||||
return idx < ACTION_MAX ? actions[idx](args) : 0;
|
||||
}
|
||||
|
||||
static int _lvmpolld_client(const char *socket, unsigned action)
|
||||
{
|
||||
int r;
|
||||
|
||||
_lvmpolld = _lvmpolld_open(socket);
|
||||
|
||||
if (_lvmpolld.error || _lvmpolld.socket_fd < 0) {
|
||||
fprintf(stderr, "Failed to establish connection with lvmpolld.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = _make_action(action, NULL);
|
||||
|
||||
daemon_close(_lvmpolld);
|
||||
|
||||
return r ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int action_idx = ACTION_MAX;
|
||||
static struct option long_options[] = {
|
||||
/* Have actions always at the beginning of the array. */
|
||||
{"dump", no_argument, &action_idx, ACTION_DUMP }, /* or an option_index ? */
|
||||
|
||||
/* other options */
|
||||
{"binary", required_argument, 0, 'B' },
|
||||
{"foreground", no_argument, 0, 'f' },
|
||||
{"help", no_argument, 0, 'h' },
|
||||
{"log", required_argument, 0, 'l' },
|
||||
{"pidfile", required_argument, 0, 'p' },
|
||||
{"socket", required_argument, 0, 's' },
|
||||
{"timeout", required_argument, 0, 't' },
|
||||
{"version", no_argument, 0, 'V' },
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
int option_index = 0;
|
||||
int client = 0, server = 0;
|
||||
unsigned action = ACTION_MAX;
|
||||
struct timeval timeout;
|
||||
daemon_idle di = { .ptimeout = &timeout };
|
||||
struct lvmpolld_state ls = { .log_config = "" };
|
||||
daemon_state s = {
|
||||
.daemon_fini = _fini,
|
||||
.daemon_init = _init,
|
||||
.handler = _handler,
|
||||
.name = "lvmpolld",
|
||||
.pidfile = getenv("LVM_LVMPOLLD_PIDFILE") ?: LVMPOLLD_PIDFILE,
|
||||
.private = &ls,
|
||||
.protocol = LVMPOLLD_PROTOCOL,
|
||||
.protocol_version = LVMPOLLD_PROTOCOL_VERSION,
|
||||
.socket_path = getenv("LVM_LVMPOLLD_SOCKET") ?: LVMPOLLD_SOCKET,
|
||||
};
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "fhVl:p:s:B:t:", long_options, &option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 0 :
|
||||
if (action < ACTION_MAX) {
|
||||
fprintf(stderr, "Can't perform more actions. Action already requested: %s\n",
|
||||
long_options[action].name);
|
||||
_usage(argv[0], stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
action = action_idx;
|
||||
client = 1;
|
||||
break;
|
||||
case '?':
|
||||
_usage(argv[0], stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
case 'B': /* --binary */
|
||||
ls.lvm_binary = optarg;
|
||||
server = 1;
|
||||
break;
|
||||
case 'V': /* --version */
|
||||
printf("lvmpolld version: " LVM_VERSION "\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'f': /* --foreground */
|
||||
s.foreground = 1;
|
||||
server = 1;
|
||||
break;
|
||||
case 'h': /* --help */
|
||||
_usage(argv[0], stdout);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'l': /* --log */
|
||||
ls.log_config = optarg;
|
||||
server = 1;
|
||||
break;
|
||||
case 'p': /* --pidfile */
|
||||
s.pidfile = optarg;
|
||||
server = 1;
|
||||
break;
|
||||
case 's': /* --socket */
|
||||
s.socket_path = optarg;
|
||||
break;
|
||||
case 't': /* --timeout in seconds */
|
||||
if (!process_timeout_arg(optarg, &di.max_timeouts)) {
|
||||
fprintf(stderr, "Invalid value of timeout parameter.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* 0 equals to wait indefinitely */
|
||||
if (di.max_timeouts)
|
||||
s.idle = ls.idle = &di;
|
||||
server = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (client && server) {
|
||||
fprintf(stderr, "Invalid combination of client and server parameters.\n\n");
|
||||
_usage(argv[0], stdout);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (client)
|
||||
return _lvmpolld_client(s.socket_path, action);
|
||||
|
||||
/* Server */
|
||||
daemon_start(s);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
391
daemons/lvmpolld/lvmpolld-data-utils.c
Normal file
391
daemons/lvmpolld/lvmpolld-data-utils.c
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lvmpolld-common.h"
|
||||
|
||||
#include "config-util.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
|
||||
static char *_construct_full_lvname(const char *vgname, const char *lvname)
|
||||
{
|
||||
char *name;
|
||||
size_t l;
|
||||
|
||||
l = strlen(vgname) + strlen(lvname) + 2; /* vg/lv and \0 */
|
||||
name = (char *) dm_malloc(l * sizeof(char));
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
if (dm_snprintf(name, l, "%s/%s", vgname, lvname) < 0) {
|
||||
dm_free(name);
|
||||
name = NULL;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static char *_construct_lvm_system_dir_env(const char *sysdir)
|
||||
{
|
||||
/*
|
||||
* Store either "LVM_SYSTEM_DIR=/path/to..."
|
||||
* - or -
|
||||
* just single char to store NULL byte
|
||||
*/
|
||||
size_t l = sysdir ? strlen(sysdir) + 16 : 1;
|
||||
char *env = (char *) dm_malloc(l * sizeof(char));
|
||||
|
||||
if (!env)
|
||||
return NULL;
|
||||
|
||||
*env = '\0';
|
||||
|
||||
if (sysdir && dm_snprintf(env, l, "LVM_SYSTEM_DIR=%s", sysdir) < 0) {
|
||||
dm_free(env);
|
||||
env = NULL;
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
static const char *_get_lvid(const char *lvmpolld_id, const char *sysdir)
|
||||
{
|
||||
return lvmpolld_id ? (lvmpolld_id + (sysdir ? strlen(sysdir) : 0)) : NULL;
|
||||
}
|
||||
|
||||
char *construct_id(const char *sysdir, const char *uuid)
|
||||
{
|
||||
char *id;
|
||||
int r;
|
||||
size_t l;
|
||||
|
||||
l = strlen(uuid) + (sysdir ? strlen(sysdir) : 0) + 1;
|
||||
id = (char *) dm_malloc(l * sizeof(char));
|
||||
if (!id)
|
||||
return NULL;
|
||||
|
||||
r = sysdir ? dm_snprintf(id, l, "%s%s", sysdir, uuid) :
|
||||
dm_snprintf(id, l, "%s", uuid);
|
||||
|
||||
if (r < 0) {
|
||||
dm_free(id);
|
||||
id = NULL;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
struct lvmpolld_lv *pdlv_create(struct lvmpolld_state *ls, const char *id,
|
||||
const char *vgname, const char *lvname,
|
||||
const char *sysdir, enum poll_type type,
|
||||
const char *sinterval, unsigned pdtimeout,
|
||||
struct lvmpolld_store *pdst)
|
||||
{
|
||||
char *lvmpolld_id = dm_strdup(id), /* copy */
|
||||
*full_lvname = _construct_full_lvname(vgname, lvname), /* copy */
|
||||
*lvm_system_dir_env = _construct_lvm_system_dir_env(sysdir); /* copy */
|
||||
|
||||
struct lvmpolld_lv tmp = {
|
||||
.ls = ls,
|
||||
.type = type,
|
||||
.lvmpolld_id = lvmpolld_id,
|
||||
.lvid = _get_lvid(lvmpolld_id, sysdir),
|
||||
.lvname = full_lvname,
|
||||
.lvm_system_dir_env = lvm_system_dir_env,
|
||||
.sinterval = dm_strdup(sinterval), /* copy */
|
||||
.pdtimeout = pdtimeout < MIN_POLLING_TIMEOUT ? MIN_POLLING_TIMEOUT : pdtimeout,
|
||||
.cmd_state = { .retcode = -1, .signal = 0 },
|
||||
.pdst = pdst,
|
||||
.init_rq_count = 1
|
||||
}, *pdlv = (struct lvmpolld_lv *) dm_malloc(sizeof(struct lvmpolld_lv));
|
||||
|
||||
if (!pdlv || !tmp.lvid || !tmp.lvname || !tmp.lvm_system_dir_env || !tmp.sinterval)
|
||||
goto err;
|
||||
|
||||
memcpy(pdlv, &tmp, sizeof(*pdlv));
|
||||
|
||||
if (pthread_mutex_init(&pdlv->lock, NULL))
|
||||
goto err;
|
||||
|
||||
return pdlv;
|
||||
|
||||
err:
|
||||
dm_free((void *)full_lvname);
|
||||
dm_free((void *)lvmpolld_id);
|
||||
dm_free((void *)lvm_system_dir_env);
|
||||
dm_free((void *)tmp.sinterval);
|
||||
dm_free((void *)pdlv);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pdlv_destroy(struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
dm_free((void *)pdlv->lvmpolld_id);
|
||||
dm_free((void *)pdlv->lvname);
|
||||
dm_free((void *)pdlv->sinterval);
|
||||
dm_free((void *)pdlv->lvm_system_dir_env);
|
||||
dm_free((void *)pdlv->cmdargv);
|
||||
dm_free((void *)pdlv->cmdenvp);
|
||||
|
||||
pthread_mutex_destroy(&pdlv->lock);
|
||||
|
||||
dm_free((void *)pdlv);
|
||||
}
|
||||
|
||||
unsigned pdlv_get_polling_finished(struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
unsigned ret;
|
||||
|
||||
pdlv_lock(pdlv);
|
||||
ret = pdlv->polling_finished;
|
||||
pdlv_unlock(pdlv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct lvmpolld_lv_state pdlv_get_status(struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
struct lvmpolld_lv_state r;
|
||||
|
||||
pdlv_lock(pdlv);
|
||||
r.error = pdlv_locked_error(pdlv);
|
||||
r.polling_finished = pdlv_locked_polling_finished(pdlv);
|
||||
r.cmd_state = pdlv_locked_cmd_state(pdlv);
|
||||
pdlv_unlock(pdlv);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void pdlv_set_cmd_state(struct lvmpolld_lv *pdlv, const struct lvmpolld_cmd_stat *cmd_state)
|
||||
{
|
||||
pdlv_lock(pdlv);
|
||||
pdlv->cmd_state = *cmd_state;
|
||||
pdlv_unlock(pdlv);
|
||||
}
|
||||
|
||||
void pdlv_set_error(struct lvmpolld_lv *pdlv, unsigned error)
|
||||
{
|
||||
pdlv_lock(pdlv);
|
||||
pdlv->error = error;
|
||||
pdlv_unlock(pdlv);
|
||||
}
|
||||
|
||||
void pdlv_set_polling_finished(struct lvmpolld_lv *pdlv, unsigned finished)
|
||||
{
|
||||
pdlv_lock(pdlv);
|
||||
pdlv->polling_finished = finished;
|
||||
pdlv_unlock(pdlv);
|
||||
}
|
||||
|
||||
struct lvmpolld_store *pdst_init(const char *name)
|
||||
{
|
||||
struct lvmpolld_store *pdst = (struct lvmpolld_store *) dm_malloc(sizeof(struct lvmpolld_store));
|
||||
if (!pdst)
|
||||
return NULL;
|
||||
|
||||
pdst->store = dm_hash_create(32);
|
||||
if (!pdst->store)
|
||||
goto err_hash;
|
||||
if (pthread_mutex_init(&pdst->lock, NULL))
|
||||
goto err_mutex;
|
||||
|
||||
pdst->name = name;
|
||||
pdst->active_polling_count = 0;
|
||||
|
||||
return pdst;
|
||||
|
||||
err_mutex:
|
||||
dm_hash_destroy(pdst->store);
|
||||
err_hash:
|
||||
dm_free(pdst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pdst_destroy(struct lvmpolld_store *pdst)
|
||||
{
|
||||
if (!pdst)
|
||||
return;
|
||||
|
||||
dm_hash_destroy(pdst->store);
|
||||
pthread_mutex_destroy(&pdst->lock);
|
||||
dm_free(pdst);
|
||||
}
|
||||
|
||||
void pdst_locked_lock_all_pdlvs(const struct lvmpolld_store *pdst)
|
||||
{
|
||||
struct dm_hash_node *n;
|
||||
|
||||
dm_hash_iterate(n, pdst->store)
|
||||
pdlv_lock(dm_hash_get_data(pdst->store, n));
|
||||
}
|
||||
|
||||
void pdst_locked_unlock_all_pdlvs(const struct lvmpolld_store *pdst)
|
||||
{
|
||||
struct dm_hash_node *n;
|
||||
|
||||
dm_hash_iterate(n, pdst->store)
|
||||
pdlv_unlock(dm_hash_get_data(pdst->store, n));
|
||||
}
|
||||
|
||||
static void _pdlv_locked_dump(struct buffer *buff, const struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
char tmp[1024];
|
||||
const struct lvmpolld_cmd_stat *cmd_state = &pdlv->cmd_state;
|
||||
|
||||
/* pdlv-section { */
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t%s {\n", pdlv->lvmpolld_id) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvid=\"%s\"\n", pdlv->lvid) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\ttype=\"%s\"\n", polling_op(pdlv->type)) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvname=\"%s\"\n", pdlv->lvname) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvmpolld_internal_timeout=%d\n", pdlv->pdtimeout) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvm_command_interval=\"%s\"\n", pdlv->sinterval ?: "<undefined>") > 0)
|
||||
buffer_append(buff, tmp);
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tLVM_SYSTEM_DIR=\"%s\"\n",
|
||||
(*pdlv->lvm_system_dir_env ? (pdlv->lvm_system_dir_env + strlen("LVM_SYSTEM_DIR=")) : "<undefined>")) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvm_command_pid=%d\n", pdlv->cmd_pid) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tpolling_finished=%d\n", pdlv->polling_finished) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\terror_occured=%d\n", pdlv->error) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tinit_requests_count=%d\n", pdlv->init_rq_count) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
|
||||
/* lvm_commmand-section { */
|
||||
buffer_append(buff, "\t\tlvm_command {\n");
|
||||
if (cmd_state->retcode == -1 && !cmd_state->signal)
|
||||
buffer_append(buff, "\t\t\tstate=\"" LVMPD_RESP_IN_PROGRESS "\"\n");
|
||||
else {
|
||||
buffer_append(buff, "\t\t\tstate=\"" LVMPD_RESP_FINISHED "\"\n");
|
||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\t\treason=\"%s\"\n\t\t\tvalue=%d\n",
|
||||
(cmd_state->signal ? LVMPD_REAS_SIGNAL : LVMPD_REAS_RETCODE),
|
||||
(cmd_state->signal ?: cmd_state->retcode)) > 0)
|
||||
buffer_append(buff, tmp);
|
||||
}
|
||||
buffer_append(buff, "\t\t}\n");
|
||||
/* } lvm_commmand-section */
|
||||
|
||||
buffer_append(buff, "\t}\n");
|
||||
/* } pdlv-section */
|
||||
}
|
||||
|
||||
void pdst_locked_dump(const struct lvmpolld_store *pdst, struct buffer *buff)
|
||||
{
|
||||
struct dm_hash_node *n;
|
||||
|
||||
dm_hash_iterate(n, pdst->store)
|
||||
_pdlv_locked_dump(buff, dm_hash_get_data(pdst->store, n));
|
||||
}
|
||||
|
||||
void pdst_locked_send_cancel(const struct lvmpolld_store *pdst)
|
||||
{
|
||||
struct lvmpolld_lv *pdlv;
|
||||
struct dm_hash_node *n;
|
||||
|
||||
dm_hash_iterate(n, pdst->store) {
|
||||
pdlv = dm_hash_get_data(pdst->store, n);
|
||||
if (!pdlv_locked_polling_finished(pdlv))
|
||||
pthread_cancel(pdlv->tid);
|
||||
}
|
||||
}
|
||||
|
||||
void pdst_locked_destroy_all_pdlvs(const struct lvmpolld_store *pdst)
|
||||
{
|
||||
struct dm_hash_node *n;
|
||||
|
||||
dm_hash_iterate(n, pdst->store)
|
||||
pdlv_destroy(dm_hash_get_data(pdst->store, n));
|
||||
}
|
||||
|
||||
struct lvmpolld_thread_data *lvmpolld_thread_data_constructor(struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
struct lvmpolld_thread_data *data = (struct lvmpolld_thread_data *) dm_malloc(sizeof(struct lvmpolld_thread_data));
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
data->pdlv = NULL;
|
||||
data->line = NULL;
|
||||
data->line_size = 0;
|
||||
data->fout = data->ferr = NULL;
|
||||
data->outpipe[0] = data->outpipe[1] = data->errpipe[0] = data->errpipe[1] = -1;
|
||||
|
||||
if (pipe(data->outpipe) || pipe(data->errpipe)) {
|
||||
lvmpolld_thread_data_destroy(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fcntl(data->outpipe[0], F_SETFD, FD_CLOEXEC) ||
|
||||
fcntl(data->outpipe[1], F_SETFD, FD_CLOEXEC) ||
|
||||
fcntl(data->errpipe[0], F_SETFD, FD_CLOEXEC) ||
|
||||
fcntl(data->errpipe[1], F_SETFD, FD_CLOEXEC)) {
|
||||
lvmpolld_thread_data_destroy(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->pdlv = pdlv;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void lvmpolld_thread_data_destroy(void *thread_private)
|
||||
{
|
||||
struct lvmpolld_thread_data *data = (struct lvmpolld_thread_data *) thread_private;
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (data->pdlv) {
|
||||
pdst_lock(data->pdlv->pdst);
|
||||
/*
|
||||
* FIXME: skip this step if lvmpolld is activated
|
||||
* by systemd.
|
||||
*/
|
||||
if (!pdlv_get_polling_finished(data->pdlv))
|
||||
kill(data->pdlv->cmd_pid, SIGTERM);
|
||||
pdlv_set_polling_finished(data->pdlv, 1);
|
||||
pdst_locked_dec(data->pdlv->pdst);
|
||||
pdst_unlock(data->pdlv->pdst);
|
||||
}
|
||||
|
||||
/* may get reallocated in getline(). dm_free must not be used */
|
||||
free(data->line);
|
||||
|
||||
if (data->fout && !fclose(data->fout))
|
||||
data->outpipe[0] = -1;
|
||||
|
||||
if (data->ferr && !fclose(data->ferr))
|
||||
data->errpipe[0] = -1;
|
||||
|
||||
if (data->outpipe[0] >= 0)
|
||||
(void) close(data->outpipe[0]);
|
||||
|
||||
if (data->outpipe[1] >= 0)
|
||||
(void) close(data->outpipe[1]);
|
||||
|
||||
if (data->errpipe[0] >= 0)
|
||||
(void) close(data->errpipe[0]);
|
||||
|
||||
if (data->errpipe[1] >= 0)
|
||||
(void) close(data->errpipe[1]);
|
||||
|
||||
dm_free(data);
|
||||
}
|
||||
215
daemons/lvmpolld/lvmpolld-data-utils.h
Normal file
215
daemons/lvmpolld/lvmpolld-data-utils.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LVMPOLLD_DATA_UTILS_H
|
||||
#define _LVM_LVMPOLLD_DATA_UTILS_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
struct buffer;
|
||||
struct lvmpolld_state;
|
||||
|
||||
enum poll_type {
|
||||
PVMOVE = 0,
|
||||
CONVERT,
|
||||
MERGE,
|
||||
MERGE_THIN,
|
||||
POLL_TYPE_MAX
|
||||
};
|
||||
|
||||
struct lvmpolld_cmd_stat {
|
||||
int retcode;
|
||||
int signal;
|
||||
};
|
||||
|
||||
struct lvmpolld_store {
|
||||
pthread_mutex_t lock;
|
||||
void *store;
|
||||
const char *name;
|
||||
unsigned active_polling_count;
|
||||
};
|
||||
|
||||
struct lvmpolld_lv {
|
||||
/*
|
||||
* accessing following vars doesn't
|
||||
* require struct lvmpolld_lv lock
|
||||
*/
|
||||
struct lvmpolld_state *const ls;
|
||||
const enum poll_type type;
|
||||
const char *const lvid;
|
||||
const char *const lvmpolld_id;
|
||||
const char *const lvname; /* full vg/lv name */
|
||||
const unsigned pdtimeout; /* in seconds */
|
||||
const char *const sinterval;
|
||||
const char *const lvm_system_dir_env;
|
||||
struct lvmpolld_store *const pdst;
|
||||
const char *const *cmdargv;
|
||||
const char *const *cmdenvp;
|
||||
|
||||
/* only used by write */
|
||||
pid_t cmd_pid;
|
||||
pthread_t tid;
|
||||
|
||||
pthread_mutex_t lock;
|
||||
|
||||
/* block of shared variables protected by lock */
|
||||
struct lvmpolld_cmd_stat cmd_state;
|
||||
unsigned init_rq_count; /* for debuging purposes only */
|
||||
unsigned polling_finished:1; /* no more updates */
|
||||
unsigned error:1; /* unrecoverable error occured in lvmpolld */
|
||||
};
|
||||
|
||||
typedef void (*lvmpolld_parse_output_fn_t) (struct lvmpolld_lv *pdlv, const char *line);
|
||||
|
||||
/* TODO: replace with configuration option */
|
||||
#define MIN_POLLING_TIMEOUT 60
|
||||
|
||||
struct lvmpolld_lv_state {
|
||||
unsigned error:1;
|
||||
unsigned polling_finished:1;
|
||||
struct lvmpolld_cmd_stat cmd_state;
|
||||
};
|
||||
|
||||
struct lvmpolld_thread_data {
|
||||
char *line;
|
||||
size_t line_size;
|
||||
int outpipe[2];
|
||||
int errpipe[2];
|
||||
FILE *fout;
|
||||
FILE *ferr;
|
||||
char buf[1024];
|
||||
struct lvmpolld_lv *pdlv;
|
||||
};
|
||||
|
||||
char *construct_id(const char *sysdir, const char *lvid);
|
||||
|
||||
/* LVMPOLLD_LV_T section */
|
||||
|
||||
/* only call with appropriate struct lvmpolld_store lock held */
|
||||
struct lvmpolld_lv *pdlv_create(struct lvmpolld_state *ls, const char *id,
|
||||
const char *vgname, const char *lvname,
|
||||
const char *sysdir, enum poll_type type,
|
||||
const char *sinterval, unsigned pdtimeout,
|
||||
struct lvmpolld_store *pdst);
|
||||
|
||||
/* only call with appropriate struct lvmpolld_store lock held */
|
||||
void pdlv_destroy(struct lvmpolld_lv *pdlv);
|
||||
|
||||
static inline void pdlv_lock(struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
pthread_mutex_lock(&pdlv->lock);
|
||||
}
|
||||
|
||||
static inline void pdlv_unlock(struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
pthread_mutex_unlock(&pdlv->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* no struct lvmpolld_lv lock required section
|
||||
*/
|
||||
static inline int pdlv_is_type(const struct lvmpolld_lv *pdlv, enum poll_type type)
|
||||
{
|
||||
return pdlv->type == type;
|
||||
}
|
||||
|
||||
static inline unsigned pdlv_get_timeout(const struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
return pdlv->pdtimeout;
|
||||
}
|
||||
|
||||
static inline enum poll_type pdlv_get_type(const struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
return pdlv->type;
|
||||
}
|
||||
|
||||
unsigned pdlv_get_polling_finished(struct lvmpolld_lv *pdlv);
|
||||
struct lvmpolld_lv_state pdlv_get_status(struct lvmpolld_lv *pdlv);
|
||||
void pdlv_set_cmd_state(struct lvmpolld_lv *pdlv, const struct lvmpolld_cmd_stat *cmd_state);
|
||||
void pdlv_set_error(struct lvmpolld_lv *pdlv, unsigned error);
|
||||
void pdlv_set_polling_finished(struct lvmpolld_lv *pdlv, unsigned finished);
|
||||
|
||||
/*
|
||||
* struct lvmpolld_lv lock required section
|
||||
*/
|
||||
static inline struct lvmpolld_cmd_stat pdlv_locked_cmd_state(const struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
return pdlv->cmd_state;
|
||||
}
|
||||
|
||||
static inline int pdlv_locked_polling_finished(const struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
return pdlv->polling_finished;
|
||||
}
|
||||
|
||||
static inline unsigned pdlv_locked_error(const struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
return pdlv->error;
|
||||
}
|
||||
|
||||
/* struct lvmpolld_store manipulation routines */
|
||||
|
||||
struct lvmpolld_store *pdst_init(const char *name);
|
||||
void pdst_destroy(struct lvmpolld_store *pdst);
|
||||
|
||||
void pdst_locked_dump(const struct lvmpolld_store *pdst, struct buffer *buff);
|
||||
void pdst_locked_lock_all_pdlvs(const struct lvmpolld_store *pdst);
|
||||
void pdst_locked_unlock_all_pdlvs(const struct lvmpolld_store *pdst);
|
||||
void pdst_locked_destroy_all_pdlvs(const struct lvmpolld_store *pdst);
|
||||
void pdst_locked_send_cancel(const struct lvmpolld_store *pdst);
|
||||
|
||||
static inline void pdst_lock(struct lvmpolld_store *pdst)
|
||||
{
|
||||
pthread_mutex_lock(&pdst->lock);
|
||||
}
|
||||
|
||||
static inline void pdst_unlock(struct lvmpolld_store *pdst)
|
||||
{
|
||||
pthread_mutex_unlock(&pdst->lock);
|
||||
}
|
||||
|
||||
static inline void pdst_locked_inc(struct lvmpolld_store *pdst)
|
||||
{
|
||||
pdst->active_polling_count++;
|
||||
}
|
||||
|
||||
static inline void pdst_locked_dec(struct lvmpolld_store *pdst)
|
||||
{
|
||||
pdst->active_polling_count--;
|
||||
}
|
||||
|
||||
static inline unsigned pdst_locked_get_active_count(const struct lvmpolld_store *pdst)
|
||||
{
|
||||
return pdst->active_polling_count;
|
||||
}
|
||||
|
||||
static inline int pdst_locked_insert(struct lvmpolld_store *pdst, const char *key, struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
return dm_hash_insert(pdst->store, key, pdlv);
|
||||
}
|
||||
|
||||
static inline struct lvmpolld_lv *pdst_locked_lookup(struct lvmpolld_store *pdst, const char *key)
|
||||
{
|
||||
return dm_hash_lookup(pdst->store, key);
|
||||
}
|
||||
|
||||
static inline void pdst_locked_remove(struct lvmpolld_store *pdst, const char *key)
|
||||
{
|
||||
dm_hash_remove(pdst->store, key);
|
||||
}
|
||||
|
||||
struct lvmpolld_thread_data *lvmpolld_thread_data_constructor(struct lvmpolld_lv *pdlv);
|
||||
void lvmpolld_thread_data_destroy(void *thread_private);
|
||||
|
||||
#endif /* _LVM_LVMPOLLD_DATA_UTILS_H */
|
||||
52
daemons/lvmpolld/lvmpolld-protocol.h
Normal file
52
daemons/lvmpolld/lvmpolld-protocol.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Red Hat, Inc.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LVMPOLLD_PROTOCOL_H
|
||||
#define _LVM_LVMPOLLD_PROTOCOL_H
|
||||
|
||||
#include "polling_ops.h"
|
||||
|
||||
#define LVMPOLLD_PROTOCOL "lvmpolld"
|
||||
#define LVMPOLLD_PROTOCOL_VERSION 1
|
||||
|
||||
#define LVMPD_REQ_CONVERT CONVERT_POLL
|
||||
#define LVMPD_REQ_DUMP "dump"
|
||||
#define LVMPD_REQ_MERGE MERGE_POLL
|
||||
#define LVMPD_REQ_MERGE_THIN MERGE_THIN_POLL
|
||||
#define LVMPD_REQ_PROGRESS "progress_info"
|
||||
#define LVMPD_REQ_PVMOVE PVMOVE_POLL
|
||||
|
||||
#define LVMPD_PARM_ABORT "abort"
|
||||
#define LVMPD_PARM_HANDLE_MISSING_PVS "handle_missing_pvs"
|
||||
#define LVMPD_PARM_INTERVAL "interval"
|
||||
#define LVMPD_PARM_LVID "lvid"
|
||||
#define LVMPD_PARM_LVNAME "lvname"
|
||||
#define LVMPD_PARM_SYSDIR "sysdir"
|
||||
#define LVMPD_PARM_VALUE "value" /* either retcode or signal value */
|
||||
#define LVMPD_PARM_VGNAME "vgname"
|
||||
|
||||
#define LVMPD_RESP_FAILED "failed"
|
||||
#define LVMPD_RESP_FINISHED "finished"
|
||||
#define LVMPD_RESP_IN_PROGRESS "in_progress"
|
||||
#define LVMPD_RESP_EINVAL "invalid"
|
||||
#define LVMPD_RESP_NOT_FOUND "not_found"
|
||||
#define LVMPD_RESP_OK "OK"
|
||||
|
||||
#define LVMPD_REAS_RETCODE "retcode" /* lvm cmd ret code */
|
||||
#define LVMPD_REAS_SIGNAL "signal" /* lvm cmd terminating singal */
|
||||
|
||||
#define LVMPD_RET_DUP_FAILED 100
|
||||
#define LVMPD_RET_EXC_FAILED 101
|
||||
|
||||
#endif /* _LVM_LVMPOLLD_PROTOCOL_H */
|
||||
25
daemons/lvmpolld/polling_ops.h
Normal file
25
daemons/lvmpolld/polling_ops.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2014-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_TOOL_POLLING_OPS_H
|
||||
#define _LVM_TOOL_POLLING_OPS_H
|
||||
|
||||
/* this file is also part of lvmpolld protocol */
|
||||
|
||||
#define PVMOVE_POLL "pvmove"
|
||||
#define CONVERT_POLL "convert"
|
||||
#define MERGE_POLL "merge"
|
||||
#define MERGE_THIN_POLL "merge_thin"
|
||||
|
||||
#endif /* _LVM_TOOL_POLLING_OPS_H */
|
||||
@@ -137,6 +137,17 @@ hosts. Overall, this is not hard, but the devil is in the details. I would
|
||||
possibly disable lvmetad for clustered volume groups in the first phase and
|
||||
only proceed when the local mode is robust and well tested.
|
||||
|
||||
With lvmlockd, lvmetad state is kept up to date by flagging either an
|
||||
individual VG as "invalid", or the global state as "invalid". When either
|
||||
the VG or the global state are read, this invalid flag is returned along
|
||||
with the data. The client command can check for this invalid state and
|
||||
decide to read the information from disk rather than use the stale cached
|
||||
data. After the latest data is read from disk, the command may choose to
|
||||
send it to lvmetad to update the cache. lvmlockd uses version numbers
|
||||
embedded in its VG and global locks to detect when cached data becomes
|
||||
invalid, and it then tells lvmetad to set the related invalid flag.
|
||||
dct, 2015-06-23
|
||||
|
||||
Protocol & co.
|
||||
--------------
|
||||
|
||||
|
||||
81
doc/lvmpolld_overview.txt
Normal file
81
doc/lvmpolld_overview.txt
Normal file
@@ -0,0 +1,81 @@
|
||||
LVM poll daemon overview
|
||||
========================
|
||||
|
||||
(last updated: 2015-05-09)
|
||||
|
||||
LVM poll daemon (lvmpolld) is the alternative for lvm2 classical polling
|
||||
mechanisms. The motivation behind new lvmpolld was to create persistent
|
||||
system service that would be more durable and transparent. It's suited
|
||||
particularly for any systemd enabled distribution.
|
||||
|
||||
Before lvmpolld any background polling process originating in a lvm2 command
|
||||
initiated inside cgroup of a systemd service could get killed if the main
|
||||
process (service) exited in such cgroup. That could lead to premature termination
|
||||
of such lvm2 polling process.
|
||||
|
||||
Also without lvmpolld there were no means to detect a particular polling process
|
||||
suited for monitoring of specific operation is already in-progress and therefore
|
||||
it's not desirable to start next one with exactly same task. lvmpolld is able to
|
||||
detect such duplicate requests and not spawn such redundant process.
|
||||
|
||||
lvmpolld is primarily targeted for systems with systemd as init process. For systems
|
||||
without systemd there's no need to install lvmpolld because there is no issue
|
||||
with observation described in second paragraph. You can still benefit from
|
||||
avoiding duplicate polling process being spawned, but without systemd lvmpolld
|
||||
can't easily be run on-demand (activated by a socket maintained by systemd).
|
||||
|
||||
lvmpolld implement shutdown on idle and can shutdown automatically when idle
|
||||
for requested time. 60 second is recommended default here. This behaviour can be
|
||||
turned off if found useless.
|
||||
|
||||
Data structures
|
||||
---------------
|
||||
|
||||
a) Logical Volume (struct lvmpolld_lv)
|
||||
|
||||
Each operation is identified by LV. Internal identifier within lvmpolld
|
||||
is full LV uuid (vg_uuid+lv_uuid) prefixed with LVM_SYSTEM_DIR if set by client.
|
||||
|
||||
such full identifier may look like:
|
||||
|
||||
"/etc/lvm/lvm.confWFd2dU67S8Av29IcJCnYzqQirdfElnxzhCdzEh7EJrfCn9R1TIQjIj58weUZDre4"
|
||||
|
||||
or without LVM_SYSTEM_DIR being set explicitly:
|
||||
|
||||
"WFd2dU67S8Av29IcJCnYzqQirdfElnxzhCdzEh7EJrfCn9R1TIQjIj58weUZDre4"
|
||||
|
||||
|
||||
LV carries various metadata about polling operation. The most significant are:
|
||||
|
||||
VG name
|
||||
LV name
|
||||
polling interval (usually --interval passed to lvm2 command or default from lvm2
|
||||
configuration)
|
||||
operation type (one of: pvmove, convert, merge, thin_merge)
|
||||
LVM_SYSTEM_DIR (if set, this is also passed among environment variables of lvpoll
|
||||
command spawned by lvmpolld)
|
||||
|
||||
b) LV stores (struct lvmpolld_store)
|
||||
|
||||
lvmpolld uses two stores for Logical volumes (struct lvmpolld_lv). One store for polling
|
||||
operations in-progress. These operations are as of now: PV move, mirror up-conversion,
|
||||
classical snapshot merge, thin snapshot merge.
|
||||
|
||||
The second store is suited only for pvmove --abort operations in-progress. Both
|
||||
stores are independent and identical LVs (pvmove /dev/sda3 and pvmove --abort /dev/sda3)
|
||||
can be run concurently from lvmpolld point of view (on lvm2 side the consistency is
|
||||
guaranteed by lvm2 locking mechanism).
|
||||
|
||||
Locking order
|
||||
-------------
|
||||
|
||||
There are two types of locks in lvmpolld. Each store has own store lock and each LV has
|
||||
own lv lock.
|
||||
|
||||
Locking order is:
|
||||
1) store lock
|
||||
2) LV lock
|
||||
|
||||
Each LV has to be inside a store. When daemon requires to take both locks it has
|
||||
to take a store lock first and LV lock has to be taken afterwards (after the
|
||||
appropriate store lock where the LV is being stored :))
|
||||
@@ -1,11 +1,15 @@
|
||||
@top_srcdir@/daemons/clvmd/clvm.h
|
||||
@top_srcdir@/daemons/dmeventd/libdevmapper-event.h
|
||||
@top_srcdir@/daemons/lvmetad/lvmetad-client.h
|
||||
@top_srcdir@/daemons/lvmpolld/lvmpolld-protocol.h
|
||||
@top_srcdir@/daemons/lvmpolld/polling_ops.h
|
||||
@top_srcdir@/daemons/lvmlockd/lvmlockd-client.h
|
||||
@top_srcdir@/liblvm/lvm2app.h
|
||||
@top_srcdir@/lib/activate/activate.h
|
||||
@top_srcdir@/lib/activate/targets.h
|
||||
@top_srcdir@/lib/cache/lvmcache.h
|
||||
@top_srcdir@/lib/cache/lvmetad.h
|
||||
@top_srcdir@/lib/locking/lvmlockd.h
|
||||
@top_srcdir@/lib/commands/toolcontext.h
|
||||
@top_srcdir@/lib/config/config.h
|
||||
@top_srcdir@/lib/config/config_settings.h
|
||||
@@ -29,6 +33,8 @@
|
||||
@top_srcdir@/lib/locking/locking.h
|
||||
@top_srcdir@/lib/log/log.h
|
||||
@top_srcdir@/lib/log/lvm-logging.h
|
||||
@top_srcdir@/lib/lvmpolld/lvmpolld-client.h
|
||||
@top_srcdir@/lib/lvmpolld/polldaemon.h
|
||||
@top_srcdir@/lib/metadata/lv.h
|
||||
@top_srcdir@/lib/metadata/lv_alloc.h
|
||||
@top_srcdir@/lib/metadata/metadata.h
|
||||
@@ -70,3 +76,4 @@
|
||||
@top_srcdir@/libdm/misc/kdev_t.h
|
||||
@top_srcdir@/po/pogen.h
|
||||
@top_srcdir@/tools/lvm2cmd.h
|
||||
@top_srcdir@/tools/tool.h
|
||||
|
||||
@@ -20,8 +20,12 @@ include $(top_builddir)/make.tmpl
|
||||
|
||||
all: .symlinks_created
|
||||
|
||||
.symlinks_created: .symlinks
|
||||
find . -maxdepth 1 -type l -exec $(RM) \{\} \;
|
||||
LINKS := $(shell find . -maxdepth 1 -type l)
|
||||
|
||||
.symlinks_created: .symlinks
|
||||
ifneq (,$(firstword $(LINKS)))
|
||||
$(RM) $(LINKS)
|
||||
endif
|
||||
for i in `cat $<`; do $(LN_S) $$i ; done
|
||||
touch $@
|
||||
|
||||
@@ -31,5 +35,5 @@ device-mapper: all
|
||||
|
||||
cflow: all
|
||||
|
||||
DISTCLEAN_TARGETS += $(shell find . -maxdepth 1 -type l)
|
||||
DISTCLEAN_TARGETS += .include_symlinks .symlinks_created .symlinks
|
||||
DISTCLEAN_TARGETS += .symlinks
|
||||
CLEAN_TARGETS += $(LINKS) .include_symlinks .symlinks_created
|
||||
|
||||
@@ -82,7 +82,6 @@ SOURCES =\
|
||||
format_text/format-text.c \
|
||||
format_text/import.c \
|
||||
format_text/import_vsn1.c \
|
||||
format_text/tags.c \
|
||||
format_text/text_label.c \
|
||||
freeseg/freeseg.c \
|
||||
label/label.c \
|
||||
@@ -123,11 +122,6 @@ SOURCES =\
|
||||
uuid/uuid.c \
|
||||
zero/zero.c
|
||||
|
||||
ifeq ("@HAVE_REALTIME@", "yes")
|
||||
SOURCES +=\
|
||||
misc/timestamp.c
|
||||
endif
|
||||
|
||||
ifeq ("@LVM1@", "internal")
|
||||
SOURCES +=\
|
||||
format1/disk-rep.c \
|
||||
@@ -196,6 +190,16 @@ ifeq ("@BUILD_LVMETAD@", "yes")
|
||||
cache/lvmetad.c
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LVMPOLLD@", "yes")
|
||||
SOURCES +=\
|
||||
lvmpolld/lvmpolld-client.c
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LVMLOCKD@", "yes")
|
||||
SOURCES +=\
|
||||
locking/lvmlockd.c
|
||||
endif
|
||||
|
||||
ifeq ("@DMEVENTD@", "yes")
|
||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd
|
||||
LIBS += -ldevmapper-event
|
||||
|
||||
@@ -180,7 +180,7 @@ int lv_passes_auto_activation_filter(struct cmd_context *cmd, struct logical_vol
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
|
||||
if (!(cn = find_config_tree_node(cmd, activation_auto_activation_volume_list_CFG, NULL))) {
|
||||
if (!(cn = find_config_tree_array(cmd, activation_auto_activation_volume_list_CFG, NULL))) {
|
||||
log_verbose("activation/auto_activation_volume_list configuration setting "
|
||||
"not defined: All logical volumes will be auto-activated.");
|
||||
return 1;
|
||||
@@ -467,7 +467,7 @@ static int _passes_activation_filter(struct cmd_context *cmd,
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
|
||||
if (!(cn = find_config_tree_node(cmd, activation_volume_list_CFG, NULL))) {
|
||||
if (!(cn = find_config_tree_array(cmd, activation_volume_list_CFG, NULL))) {
|
||||
log_verbose("activation/volume_list configuration setting "
|
||||
"not defined: Checking only host tags for %s/%s",
|
||||
lv->vg->name, lv->name);
|
||||
@@ -496,7 +496,7 @@ static int _passes_readonly_filter(struct cmd_context *cmd,
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
|
||||
if (!(cn = find_config_tree_node(cmd, activation_read_only_volume_list_CFG, NULL)))
|
||||
if (!(cn = find_config_tree_array(cmd, activation_read_only_volume_list_CFG, NULL)))
|
||||
return 0;
|
||||
|
||||
return _lv_passes_volumes_filter(cmd, lv, cn, activation_read_only_volume_list_CFG);
|
||||
@@ -648,8 +648,8 @@ static int _lv_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
* in progress - as only those could lead to opened files
|
||||
*/
|
||||
if (with_open_count) {
|
||||
if (locking_is_clustered())
|
||||
sync_local_dev_names(cmd); /* Wait to have udev in sync */
|
||||
if (locking_is_clustered() && !sync_local_dev_names(cmd)) /* Wait to have udev in sync */
|
||||
return_0;
|
||||
else if (fs_has_non_delete_ops())
|
||||
fs_unlock(); /* For non clustered - wait if there are non-delete ops */
|
||||
}
|
||||
@@ -1813,7 +1813,9 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
|
||||
goto_out;
|
||||
|
||||
/* Ignore origin_only unless LV is origin in both old and new metadata */
|
||||
if (!lv_is_thin_volume(ondisk_lv) && !(lv_is_origin(ondisk_lv) && lv_is_origin(incore_lv)))
|
||||
/* or LV is thin or thin pool volume */
|
||||
if (!lv_is_thin_volume(ondisk_lv) && !lv_is_thin_pool(ondisk_lv) &&
|
||||
!(lv_is_origin(ondisk_lv) && lv_is_origin(incore_lv)))
|
||||
laopts->origin_only = 0;
|
||||
|
||||
if (test_mode()) {
|
||||
@@ -1987,7 +1989,6 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
|
||||
const struct logical_volume *lv_to_free = NULL;
|
||||
struct lvinfo info;
|
||||
int r = 0;
|
||||
int messages_only = 0;
|
||||
|
||||
if (!activation())
|
||||
return 1;
|
||||
@@ -1995,10 +1996,7 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
|
||||
if (!lv && !(lv_to_free = lv = lv_from_lvid(cmd, lvid_s, 0)))
|
||||
goto_out;
|
||||
|
||||
if (lv_is_thin_pool(lv) && laopts->origin_only)
|
||||
messages_only = 1;
|
||||
|
||||
if (!lv_is_origin(lv) && !lv_is_thin_volume(lv))
|
||||
if (!lv_is_origin(lv) && !lv_is_thin_volume(lv) && !lv_is_thin_pool(lv))
|
||||
laopts->origin_only = 0;
|
||||
|
||||
if (test_mode()) {
|
||||
@@ -2010,13 +2008,15 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
|
||||
|
||||
log_debug_activation("Resuming LV %s/%s%s%s%s.", lv->vg->name, lv->name,
|
||||
error_if_not_active ? "" : " if active",
|
||||
laopts->origin_only ? " without snapshots" : "",
|
||||
laopts->origin_only ?
|
||||
(lv_is_thin_pool(lv) ? " pool only" :
|
||||
lv_is_thin_volume(lv) ? " thin only" : " without snapshots") : "",
|
||||
laopts->revert ? " (reverting)" : "");
|
||||
|
||||
if (!lv_info(cmd, lv, laopts->origin_only, &info, 0, 0))
|
||||
goto_out;
|
||||
|
||||
if (!info.exists || !(info.suspended || messages_only)) {
|
||||
if (!info.exists || !info.suspended) {
|
||||
if (error_if_not_active)
|
||||
goto_out;
|
||||
r = 1;
|
||||
@@ -2272,6 +2272,7 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
|
||||
if (info.exists && !info.suspended && info.live_table &&
|
||||
(info.read_only == read_only_lv(lv, laopts))) {
|
||||
r = 1;
|
||||
log_debug_activation("Volume is already active.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ struct dev_manager {
|
||||
uint32_t pvmove_mirror_count;
|
||||
int flush_required;
|
||||
int activation; /* building activation tree */
|
||||
int suspend; /* building suspend tree */
|
||||
int skip_external_lv;
|
||||
struct dm_list pending_delete; /* str_list of dlid(s) with pending delete */
|
||||
unsigned track_pending_delete;
|
||||
@@ -445,6 +446,78 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _device_is_suspended(int major, int minor)
|
||||
{
|
||||
struct dm_task *dmt;
|
||||
struct dm_info info;
|
||||
int r = 0;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
|
||||
return 0;
|
||||
|
||||
if (!dm_task_set_major_minor(dmt, major, minor, 1))
|
||||
goto_out;
|
||||
|
||||
if (activation_checks() && !dm_task_enable_checks(dmt))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt) ||
|
||||
!dm_task_get_info(dmt, &info)) {
|
||||
log_error("Failed to get info for device %d:%d", major, minor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = info.exists && info.suspended;
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _ignore_suspended_snapshot_component(struct device *dev)
|
||||
{
|
||||
struct dm_task *dmt;
|
||||
void *next = NULL;
|
||||
char *params, *target_type = NULL;
|
||||
uint64_t start, length;
|
||||
int major1, minor1, major2, minor2;
|
||||
int r = 0;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_set_major_minor(dmt, MAJOR(dev->dev), MINOR(dev->dev), 1))
|
||||
goto_out;
|
||||
|
||||
if (activation_checks() && !dm_task_enable_checks(dmt))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
log_error("Failed to get state of snapshot or snapshot origin device");
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
if (!strcmp(target_type, "snapshot")) {
|
||||
if (sscanf(params, "%d:%d %d:%d", &major1, &minor1, &major2, &minor2) != 4) {
|
||||
log_error("Incorrect snapshot table found");
|
||||
goto_out;
|
||||
}
|
||||
r = r || _device_is_suspended(major1, minor1) || _device_is_suspended(major2, minor2);
|
||||
} else if (!strcmp(target_type, "snapshot-origin")) {
|
||||
if (sscanf(params, "%d:%d", &major1, &minor1) != 2) {
|
||||
log_error("Incorrect snapshot-origin table found");
|
||||
goto_out;
|
||||
}
|
||||
r = r || _device_is_suspended(major1, minor1);
|
||||
}
|
||||
} while (next);
|
||||
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* device_is_usable
|
||||
* @dev
|
||||
@@ -553,15 +626,25 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
* supported anymore and in general using mirrors in a stack
|
||||
* is disabled by default (with a warning that if enabled,
|
||||
* it could cause various deadlocks).
|
||||
* This is former check used, but it's not correct as it
|
||||
* disables snapshot-origins to be used in a stack in
|
||||
* general, not just over mirrors!
|
||||
* Similar situation can happen with RAID devices where
|
||||
* a RAID device can be snapshotted.
|
||||
* If one of the RAID legs are down and we're doing
|
||||
* lvconvert --repair, there's a time period in which
|
||||
* snapshot components are (besides other devs) suspended.
|
||||
* See also https://bugzilla.redhat.com/show_bug.cgi?id=1219222
|
||||
* for an example where this causes problems.
|
||||
*
|
||||
* This is a quick check for now, but replace it with more
|
||||
* robust and better check that would check the stack
|
||||
* correctly, not just snapshots but any cobimnation possible
|
||||
* in a stack - use proper dm tree to check this instead.
|
||||
*/
|
||||
/*if (check.check_suspended && target_type && !strcmp(target_type, "snapshot-origin")) {
|
||||
log_debug_activation("%s: Snapshot-origin device %s not usable.",
|
||||
dev_name(dev), name);
|
||||
if (check.check_suspended && target_type &&
|
||||
(!strcmp(target_type, "snapshot") || !strcmp(target_type, "snapshot-origin")) &&
|
||||
_ignore_suspended_snapshot_component(dev)) {
|
||||
log_debug_activation("%s: %s device %s not usable.", dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}*/
|
||||
}
|
||||
|
||||
if (target_type && strcmp(target_type, "error"))
|
||||
only_error_target = 0;
|
||||
@@ -1835,7 +1918,6 @@ struct pool_cb_data {
|
||||
int skip_zero; /* to skip zeroed device header (check first 64B) */
|
||||
int exec; /* which binary to call */
|
||||
int opts;
|
||||
const char *defaults;
|
||||
const char *global;
|
||||
};
|
||||
|
||||
@@ -1843,7 +1925,6 @@ static int _pool_callback(struct dm_tree_node *node,
|
||||
dm_node_callback_t type, void *cb_data)
|
||||
{
|
||||
int ret, status, fd;
|
||||
char *split;
|
||||
const struct dm_config_node *cn;
|
||||
const struct dm_config_value *cv;
|
||||
const struct pool_cb_data *data = cb_data;
|
||||
@@ -1858,23 +1939,19 @@ static int _pool_callback(struct dm_tree_node *node,
|
||||
if (!*argv[0])
|
||||
return 1; /* Checking disabled */
|
||||
|
||||
if ((cn = find_config_tree_node(mlv->vg->cmd, data->opts, NULL))) {
|
||||
for (cv = cn->v; cv && args < 16; cv = cv->next) {
|
||||
if (cv->type != DM_CFG_STRING) {
|
||||
log_error("Invalid string in config file: "
|
||||
"global/%s_check_options",
|
||||
data->global);
|
||||
return 0;
|
||||
}
|
||||
argv[++args] = cv->v.str;
|
||||
}
|
||||
} else {
|
||||
/* Use default options (no support for options with spaces) */
|
||||
if (!(split = dm_pool_strdup(data->dm->mem, data->defaults))) {
|
||||
log_error("Failed to duplicate defaults.");
|
||||
if (!(cn = find_config_tree_array(mlv->vg->cmd, data->opts, NULL))) {
|
||||
log_error(INTERNAL_ERROR "Unable to find configuration for pool check options.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (cv = cn->v; cv && args < 16; cv = cv->next) {
|
||||
if (cv->type != DM_CFG_STRING) {
|
||||
log_error("Invalid string in config file: "
|
||||
"global/%s_check_options",
|
||||
data->global);
|
||||
return 0;
|
||||
}
|
||||
args = dm_split_words(split, 16, 0, (char**) argv + 1);
|
||||
argv[++args] = cv->v.str;
|
||||
}
|
||||
|
||||
if (args == 16) {
|
||||
@@ -1965,14 +2042,12 @@ static int _pool_register_callback(struct dev_manager *dm,
|
||||
data->skip_zero = 1;
|
||||
data->exec = global_thin_check_executable_CFG;
|
||||
data->opts = global_thin_check_options_CFG;
|
||||
data->defaults = DEFAULT_THIN_CHECK_OPTION1 " " DEFAULT_THIN_CHECK_OPTION2;
|
||||
data->global = "thin";
|
||||
} else if (lv_is_cache(lv)) { /* cache pool */
|
||||
data->pool_lv = first_seg(lv)->pool_lv;
|
||||
data->skip_zero = dm->activation;
|
||||
data->exec = global_cache_check_executable_CFG;
|
||||
data->opts = global_cache_check_options_CFG;
|
||||
data->defaults = DEFAULT_CACHE_CHECK_OPTION1;
|
||||
data->global = "cache";
|
||||
} else {
|
||||
log_error(INTERNAL_ERROR "Registering unsupported pool callback.");
|
||||
@@ -1984,6 +2059,11 @@ static int _pool_register_callback(struct dev_manager *dm,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Declaration to resolve suspend tree and message passing for thin-pool */
|
||||
static int _add_target_to_dtree(struct dev_manager *dm,
|
||||
struct dm_tree_node *dnode,
|
||||
struct lv_segment *seg,
|
||||
struct lv_activate_opts *laopts);
|
||||
/*
|
||||
* Add LV and any known dependencies
|
||||
*/
|
||||
@@ -2052,15 +2132,43 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
*/
|
||||
if (!_add_dev_to_dtree(dm, dtree, lv, lv_layer(lv)))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* TODO: change API and move this code
|
||||
* Could be easier to handle this in _add_dev_to_dtree()
|
||||
* and base this according to info.exists ?
|
||||
*/
|
||||
if (!dm->activation) {
|
||||
/* Setup callback for non-activation partial tree */
|
||||
/* Activation gets own callback when needed */
|
||||
/* TODO: extend _cached_dm_info() to return dnode */
|
||||
if (!(uuid = build_dm_uuid(dm->mem, lv, lv_layer(lv))))
|
||||
return_0;
|
||||
if ((node = dm_tree_find_node_by_uuid(dtree, uuid)) &&
|
||||
!_pool_register_callback(dm, node, lv))
|
||||
return_0;
|
||||
if ((node = dm_tree_find_node_by_uuid(dtree, uuid))) {
|
||||
if (origin_only) {
|
||||
struct lv_activate_opts laopts = {
|
||||
.origin_only = 1,
|
||||
.send_messages = 1 /* Node with messages */
|
||||
};
|
||||
/*
|
||||
* Add some messsages if right node exist in the table only
|
||||
* when building SUSPEND tree for origin-only thin-pool.
|
||||
*
|
||||
* TODO: Fix call of '_add_target_to_dtree()' to add message
|
||||
* to thin-pool node as we already know the pool node exists
|
||||
* in the table. Any better/cleaner API way ?
|
||||
*
|
||||
* Probably some 'new' target method to add messages for any node?
|
||||
*/
|
||||
if (dm->suspend &&
|
||||
!dm_list_empty(&(first_seg(lv)->thin_messages)) &&
|
||||
!_add_target_to_dtree(dm, node, first_seg(lv), &laopts))
|
||||
return_0;
|
||||
} else {
|
||||
/* Setup callback for non-activation partial tree */
|
||||
/* Activation gets own callback when needed */
|
||||
/* TODO: extend _cached_dm_info() to return dnode */
|
||||
if (!_pool_register_callback(dm, node, lv))
|
||||
return_0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2158,7 +2266,7 @@ static struct dm_tree *_create_partial_dtree(struct dev_manager *dm, const struc
|
||||
|
||||
dm_tree_set_optional_uuid_suffixes(dtree, &uuid_suffix_list[0]);
|
||||
|
||||
if (!_add_lv_to_dtree(dm, dtree, lv, (lv_is_origin(lv) || lv_is_thin_volume(lv)) ? origin_only : 0))
|
||||
if (!_add_lv_to_dtree(dm, dtree, lv, (lv_is_origin(lv) || lv_is_thin_volume(lv) || lv_is_thin_pool(lv)) ? origin_only : 0))
|
||||
goto_bad;
|
||||
|
||||
return dtree;
|
||||
@@ -2613,7 +2721,7 @@ static int _add_segment_to_dtree(struct dev_manager *dm,
|
||||
return_0;
|
||||
|
||||
/* Add pool layer */
|
||||
if (seg->pool_lv &&
|
||||
if (seg->pool_lv && !laopts->origin_only &&
|
||||
!_add_new_lv_to_dtree(dm, dtree, seg->pool_lv, laopts,
|
||||
lv_layer(seg->pool_lv)))
|
||||
return_0;
|
||||
@@ -3042,7 +3150,10 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
int r = 0;
|
||||
|
||||
if (action < DM_ARRAY_SIZE(_action_names))
|
||||
log_debug_activation("Creating %s tree for %s.", _action_names[action], lv->name);
|
||||
log_debug_activation("Creating %s%s tree for %s.",
|
||||
_action_names[action],
|
||||
(laopts->origin_only) ? " origin-only" : "",
|
||||
display_lvname(lv));
|
||||
|
||||
/* Some LV can be used for top level tree */
|
||||
/* TODO: add more.... */
|
||||
@@ -3052,6 +3163,7 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
}
|
||||
/* Some targets may build bigger tree for activation */
|
||||
dm->activation = ((action == PRELOAD) || (action == ACTIVATE));
|
||||
dm->suspend = (action == SUSPEND_WITH_LOCKFS) || (action == SUSPEND);
|
||||
if (!(dtree = _create_partial_dtree(dm, lv, laopts->origin_only)))
|
||||
return_0;
|
||||
|
||||
@@ -3096,7 +3208,9 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
case PRELOAD:
|
||||
case ACTIVATE:
|
||||
/* Add all required new devices to tree */
|
||||
if (!_add_new_lv_to_dtree(dm, dtree, lv, laopts, (lv_is_origin(lv) && laopts->origin_only) ? "real" : NULL))
|
||||
if (!_add_new_lv_to_dtree(dm, dtree, lv, laopts,
|
||||
(lv_is_origin(lv) && laopts->origin_only) ? "real" :
|
||||
(lv_is_thin_pool(lv) && laopts->origin_only) ? "tpool" : NULL))
|
||||
goto_out;
|
||||
|
||||
/* Preload any devices required before any suspensions */
|
||||
@@ -3134,7 +3248,6 @@ out_no_root:
|
||||
int dev_manager_activate(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
struct lv_activate_opts *laopts)
|
||||
{
|
||||
laopts->send_messages = 1;
|
||||
if (!_tree_action(dm, lv, laopts, ACTIVATE))
|
||||
return_0;
|
||||
|
||||
|
||||
379
lib/cache/lvmcache.c
vendored
379
lib/cache/lvmcache.c
vendored
@@ -56,6 +56,7 @@ struct lvmcache_vginfo {
|
||||
char _padding[7];
|
||||
struct lvmcache_vginfo *next; /* Another VG with same name? */
|
||||
char *creation_host;
|
||||
char *lock_type;
|
||||
uint32_t mda_checksum;
|
||||
size_t mda_size;
|
||||
size_t vgmetadata_size;
|
||||
@@ -67,6 +68,7 @@ struct lvmcache_vginfo {
|
||||
unsigned vg_use_count; /* Counter of vg reusage */
|
||||
unsigned precommitted; /* Is vgmetadata live or precommitted? */
|
||||
unsigned cached_vg_invalidated; /* Signal to regenerate cached_vg */
|
||||
unsigned preferred_duplicates; /* preferred duplicate pvs have been set */
|
||||
};
|
||||
|
||||
static struct dm_hash_table *_pvid_hash = NULL;
|
||||
@@ -115,6 +117,47 @@ int lvmcache_init(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Once PV info has been populated in lvmcache and
|
||||
* lvmcache has chosen preferred duplicate devices,
|
||||
* set this flag so that lvmcache will not try to
|
||||
* compare and choose preferred duplicate devices
|
||||
* again (which may result in different preferred
|
||||
* devices.) PV info can be populated in lvmcache
|
||||
* multiple times, each time causing lvmcache to
|
||||
* compare the duplicate devices, so we need to
|
||||
* record that the comparison/preferences have
|
||||
* already been done, so the preferrences from the
|
||||
* first time through are not changed.
|
||||
*
|
||||
* This is something of a hack to work around the
|
||||
* fact that the code isn't really designed to
|
||||
* handle duplicate PVs, and the fact that lvmetad
|
||||
* has its own way of picking a preferred duplicate
|
||||
* and lvmcache has another way based on having
|
||||
* more information than lvmetad does.
|
||||
*
|
||||
* If we come up with a better overall method to
|
||||
* handle duplicate PVs, then this can probably be
|
||||
* removed.
|
||||
*
|
||||
* FIXME: if we want to make lvmetad work with clvmd,
|
||||
* then this may need to be changed to set
|
||||
* preferred_duplicates back to 0.
|
||||
*/
|
||||
|
||||
void lvmcache_set_preferred_duplicates(const char *vgid)
|
||||
{
|
||||
struct lvmcache_vginfo *vginfo;
|
||||
|
||||
if (!(vginfo = lvmcache_vginfo_from_vgid(vgid))) {
|
||||
stack;
|
||||
return;
|
||||
}
|
||||
|
||||
vginfo->preferred_duplicates = 1;
|
||||
}
|
||||
|
||||
void lvmcache_seed_infos_from_lvmetad(struct cmd_context *cmd)
|
||||
{
|
||||
if (!lvmetad_active() || _has_scanned)
|
||||
@@ -863,6 +906,37 @@ int lvmcache_vginfo_holders_dec_and_test_for_zero(struct lvmcache_vginfo *vginfo
|
||||
}
|
||||
// #endif
|
||||
|
||||
int lvmcache_get_vgnameids(struct cmd_context *cmd, int include_internal,
|
||||
struct dm_list *vgnameids)
|
||||
{
|
||||
struct vgnameid_list *vgnl;
|
||||
struct lvmcache_vginfo *vginfo;
|
||||
|
||||
lvmcache_label_scan(cmd, 0);
|
||||
|
||||
dm_list_iterate_items(vginfo, &_vginfos) {
|
||||
if (!include_internal && is_orphan_vg(vginfo->vgname))
|
||||
continue;
|
||||
|
||||
if (!(vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl)))) {
|
||||
log_error("vgnameid_list allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vgnl->vgid = dm_pool_strdup(cmd->mem, vginfo->vgid);
|
||||
vgnl->vg_name = dm_pool_strdup(cmd->mem, vginfo->vgname);
|
||||
|
||||
if (!vgnl->vgid || !vgnl->vg_name) {
|
||||
log_error("vgnameid_list member allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_list_add(vgnameids, &vgnl->list);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct dm_list *lvmcache_get_vgids(struct cmd_context *cmd,
|
||||
int include_internal)
|
||||
{
|
||||
@@ -1374,7 +1448,7 @@ static int _lvmcache_update_vgname(struct lvmcache_info *info,
|
||||
}
|
||||
|
||||
static int _lvmcache_update_vgstatus(struct lvmcache_info *info, uint32_t vgstatus,
|
||||
const char *creation_host)
|
||||
const char *creation_host, const char *lock_type)
|
||||
{
|
||||
if (!info || !info->vginfo)
|
||||
return 1;
|
||||
@@ -1387,11 +1461,11 @@ static int _lvmcache_update_vgstatus(struct lvmcache_info *info, uint32_t vgstat
|
||||
info->vginfo->status = vgstatus;
|
||||
|
||||
if (!creation_host)
|
||||
return 1;
|
||||
goto set_lock_type;
|
||||
|
||||
if (info->vginfo->creation_host && !strcmp(creation_host,
|
||||
info->vginfo->creation_host))
|
||||
return 1;
|
||||
goto set_lock_type;
|
||||
|
||||
if (info->vginfo->creation_host)
|
||||
dm_free(info->vginfo->creation_host);
|
||||
@@ -1405,6 +1479,24 @@ static int _lvmcache_update_vgstatus(struct lvmcache_info *info, uint32_t vgstat
|
||||
log_debug_cache("lvmcache: %s: VG %s: Set creation host to %s.",
|
||||
dev_name(info->dev), info->vginfo->vgname, creation_host);
|
||||
|
||||
set_lock_type:
|
||||
|
||||
if (!lock_type)
|
||||
goto out;
|
||||
|
||||
if (info->vginfo->lock_type && !strcmp(lock_type, info->vginfo->lock_type))
|
||||
goto out;
|
||||
|
||||
if (info->vginfo->lock_type)
|
||||
dm_free(info->vginfo->lock_type);
|
||||
|
||||
if (!(info->vginfo->lock_type = dm_strdup(lock_type))) {
|
||||
log_error("cache creation host alloc failed for %s",
|
||||
lock_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1473,7 +1565,7 @@ int lvmcache_update_vgname_and_id(struct lvmcache_info *info, struct lvmcache_vg
|
||||
if (!_lvmcache_update_vgname(info, vgname, vgid, vgsummary->vgstatus,
|
||||
vgsummary->creation_host, info->fmt) ||
|
||||
!_lvmcache_update_vgid(info, info->vginfo, vgid) ||
|
||||
!_lvmcache_update_vgstatus(info, vgsummary->vgstatus, vgsummary->creation_host) ||
|
||||
!_lvmcache_update_vgstatus(info, vgsummary->vgstatus, vgsummary->creation_host, vgsummary->lock_type) ||
|
||||
!_lvmcache_update_vg_mda_info(info, vgsummary->mda_checksum, vgsummary->mda_size))
|
||||
return_0;
|
||||
|
||||
@@ -1488,7 +1580,8 @@ int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted)
|
||||
struct lvmcache_vgsummary vgsummary = {
|
||||
.vgname = vg->name,
|
||||
.vgstatus = vg->status,
|
||||
.vgid = vg->id
|
||||
.vgid = vg->id,
|
||||
.lock_type = vg->lock_type
|
||||
};
|
||||
|
||||
pvid_s[sizeof(pvid_s) - 1] = '\0';
|
||||
@@ -1529,6 +1622,64 @@ void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv,
|
||||
pv->dev = dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can see multiple different devices with the
|
||||
* same pvid, i.e. duplicates.
|
||||
*
|
||||
* There may be different reasons for seeing two
|
||||
* devices with the same pvid:
|
||||
* - multipath showing two paths to the same thing
|
||||
* - one device copied to another, e.g. with dd,
|
||||
* also referred to as cloned devices.
|
||||
* - a "subsystem" taking a device and creating
|
||||
* another device of its own that represents the
|
||||
* underlying device it is using, e.g. using dm
|
||||
* to create an identity mapping of a PV.
|
||||
*
|
||||
* Given duplicate devices, we have to choose one
|
||||
* of them to be the "preferred" dev, i.e. the one
|
||||
* that will be referenced in lvmcache, by pv->dev.
|
||||
* We can keep the existing dev, that's currently
|
||||
* used in lvmcache, or we can replace the existing
|
||||
* dev with the new duplicate.
|
||||
*
|
||||
* Regardless of which device is preferred, we need
|
||||
* to print messages explaining which devices were
|
||||
* found so that a user can sort out for themselves
|
||||
* what has happened if the preferred device is not
|
||||
* the one they are interested in.
|
||||
*
|
||||
* If a user wants to use the non-preferred device,
|
||||
* they will need to filter out the device that
|
||||
* lvm is preferring.
|
||||
*
|
||||
* The dev_subsystem calls check if the major number
|
||||
* of the dev is part of a subsystem like DM/MD/DRBD.
|
||||
* A dev that's part of a subsystem is preferred over a
|
||||
* duplicate of that dev that is not part of a
|
||||
* subsystem.
|
||||
*
|
||||
* The has_holders calls check if the device is being
|
||||
* used by another, and prefers one that's being used.
|
||||
*
|
||||
* FIXME: why do we prefer a device without holders
|
||||
* over a device with holders? We should understand
|
||||
* the reason for that choice.
|
||||
*
|
||||
* FIXME: there may be other reasons to prefer one
|
||||
* device over another:
|
||||
*
|
||||
* . are there other use/open counts we could check
|
||||
* beyond the holders?
|
||||
*
|
||||
* . check if either is bad/usable and prefer
|
||||
* the good one?
|
||||
*
|
||||
* . prefer the one with smaller minor number?
|
||||
* Might avoid disturbing things due to a new
|
||||
* transient duplicate?
|
||||
*/
|
||||
|
||||
struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
|
||||
struct device *dev,
|
||||
const char *vgname, const char *vgid,
|
||||
@@ -1576,54 +1727,166 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
|
||||
lvmcache_del_bas(info);
|
||||
} else {
|
||||
if (existing->dev != dev) {
|
||||
/* Is the existing entry a duplicate pvid e.g. md ? */
|
||||
if (dev_subsystem_part_major(dt, existing->dev) &&
|
||||
!dev_subsystem_part_major(dt, dev)) {
|
||||
log_very_verbose("Ignoring duplicate PV %s on "
|
||||
"%s - using %s %s",
|
||||
pvid, dev_name(dev),
|
||||
dev_subsystem_name(dt, existing->dev),
|
||||
dev_name(existing->dev));
|
||||
return NULL;
|
||||
} else if (dm_is_dm_major(MAJOR(existing->dev->dev)) &&
|
||||
!dm_is_dm_major(MAJOR(dev->dev))) {
|
||||
log_very_verbose("Ignoring duplicate PV %s on "
|
||||
"%s - using dm %s",
|
||||
pvid, dev_name(dev),
|
||||
dev_name(existing->dev));
|
||||
return NULL;
|
||||
} else if (!dev_subsystem_part_major(dt, existing->dev) &&
|
||||
dev_subsystem_part_major(dt, dev))
|
||||
log_very_verbose("Duplicate PV %s on %s - "
|
||||
"using %s %s", pvid,
|
||||
dev_name(existing->dev),
|
||||
dev_subsystem_name(dt, existing->dev),
|
||||
dev_name(dev));
|
||||
else if (!dm_is_dm_major(MAJOR(existing->dev->dev)) &&
|
||||
dm_is_dm_major(MAJOR(dev->dev)))
|
||||
log_very_verbose("Duplicate PV %s on %s - "
|
||||
"using dm %s", pvid,
|
||||
dev_name(existing->dev),
|
||||
dev_name(dev));
|
||||
/* FIXME If both dm, check dependencies */
|
||||
//else if (dm_is_dm_major(MAJOR(existing->dev->dev)) &&
|
||||
//dm_is_dm_major(MAJOR(dev->dev)))
|
||||
//
|
||||
else if (!strcmp(pvid_s, existing->dev->pvid)) {
|
||||
log_error("Found duplicate PV %s: using %s not %s",
|
||||
pvid_s,
|
||||
dev_name(existing->dev),
|
||||
dev_name(dev));
|
||||
strncpy(dev->pvid, pvid_s, sizeof(dev->pvid));
|
||||
_found_duplicate_pvs = 1;
|
||||
int old_in_subsystem = 0;
|
||||
int new_in_subsystem = 0;
|
||||
int old_is_dm = 0;
|
||||
int new_is_dm = 0;
|
||||
int old_has_holders = 0;
|
||||
int new_has_holders = 0;
|
||||
|
||||
/*
|
||||
* Here are different devices with the same pvid:
|
||||
* duplicates. See comment above.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This flag tells the process_each_pv code to search
|
||||
* the devices list for duplicates, so that devices
|
||||
* can be processed together with their duplicates
|
||||
* (while processing the VG, rather than reporting
|
||||
* pv->dev under the VG, and its duplicate outside
|
||||
* the VG context.)
|
||||
*/
|
||||
_found_duplicate_pvs = 1;
|
||||
|
||||
/*
|
||||
* The new dev may not have pvid set.
|
||||
* The process_each_pv code needs to have the pvid
|
||||
* set in each device to detect that the devices
|
||||
* are duplicates.
|
||||
*/
|
||||
strncpy(dev->pvid, pvid_s, sizeof(dev->pvid));
|
||||
|
||||
/*
|
||||
* Now decide if we are going to ignore the new
|
||||
* device, or replace the existing/old device in
|
||||
* lvmcache with the new one.
|
||||
*/
|
||||
old_in_subsystem = dev_subsystem_part_major(dt, existing->dev);
|
||||
new_in_subsystem = dev_subsystem_part_major(dt, dev);
|
||||
|
||||
old_is_dm = dm_is_dm_major(MAJOR(existing->dev->dev));
|
||||
new_is_dm = dm_is_dm_major(MAJOR(dev->dev));
|
||||
|
||||
old_has_holders = dm_device_has_holders(MAJOR(existing->dev->dev), MINOR(existing->dev->dev));
|
||||
new_has_holders = dm_device_has_holders(MAJOR(dev->dev), MINOR(dev->dev));
|
||||
|
||||
if (old_has_holders && new_has_holders) {
|
||||
/*
|
||||
* This is not a selection of old or new, but
|
||||
* just a warning to be aware of.
|
||||
*/
|
||||
log_warn("WARNING: duplicate PV %s is being used from both devices %s and %s",
|
||||
pvid_s,
|
||||
dev_name(existing->dev),
|
||||
dev_name(dev));
|
||||
}
|
||||
|
||||
if (existing->vginfo->preferred_duplicates) {
|
||||
/*
|
||||
* The preferred duplicate devs have already
|
||||
* been chosen during a previous populating of
|
||||
* lvmcache, so just use the existing preferences.
|
||||
*/
|
||||
log_verbose("Found duplicate PV %s: using existing dev %s",
|
||||
pvid_s,
|
||||
dev_name(existing->dev));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (old_in_subsystem && !new_in_subsystem) {
|
||||
/* Use old, ignore new. */
|
||||
log_warn("Found duplicate PV %s: using %s not %s",
|
||||
pvid_s,
|
||||
dev_name(existing->dev),
|
||||
dev_name(dev));
|
||||
log_warn("Using duplicate PV %s from subsystem %s, ignoring %s",
|
||||
dev_name(existing->dev),
|
||||
dev_subsystem_name(dt, existing->dev),
|
||||
dev_name(dev));
|
||||
return NULL;
|
||||
|
||||
} else if (!old_in_subsystem && new_in_subsystem) {
|
||||
/* Use new, replace old. */
|
||||
log_warn("Found duplicate PV %s: using %s not %s",
|
||||
pvid_s,
|
||||
dev_name(dev),
|
||||
dev_name(existing->dev));
|
||||
log_warn("Using duplicate PV %s from subsystem %s, replacing %s",
|
||||
dev_name(dev),
|
||||
dev_subsystem_name(dt, dev),
|
||||
dev_name(existing->dev));
|
||||
|
||||
} else if (old_has_holders && !new_has_holders) {
|
||||
/* Use new, replace old. */
|
||||
/* FIXME: why choose the one without olders? */
|
||||
log_warn("Found duplicate PV %s: using %s not %s",
|
||||
pvid_s,
|
||||
dev_name(dev),
|
||||
dev_name(existing->dev));
|
||||
log_warn("Using duplicate PV %s without holders, replacing %s",
|
||||
dev_name(dev),
|
||||
dev_name(existing->dev));
|
||||
|
||||
} else if (!old_has_holders && new_has_holders) {
|
||||
/* Use old, ignore new. */
|
||||
log_warn("Found duplicate PV %s: using %s not %s",
|
||||
pvid_s,
|
||||
dev_name(existing->dev),
|
||||
dev_name(dev));
|
||||
log_warn("Using duplicate PV %s without holders, ignoring %s",
|
||||
dev_name(existing->dev),
|
||||
dev_name(dev));
|
||||
return NULL;
|
||||
|
||||
} else if (old_is_dm && new_is_dm) {
|
||||
/* Use new, replace old. */
|
||||
/* FIXME: why choose the new instead of the old? */
|
||||
log_warn("Found duplicate PV %s: using %s not %s",
|
||||
pvid_s,
|
||||
dev_name(dev),
|
||||
dev_name(existing->dev));
|
||||
log_warn("Using duplicate PV %s which is last seen, replacing %s",
|
||||
dev_name(dev),
|
||||
dev_name(existing->dev));
|
||||
|
||||
} else if (!strcmp(pvid_s, existing->dev->pvid)) {
|
||||
/* No criteria to use for preferring old or new. */
|
||||
/* FIXME: why choose the new instead of the old? */
|
||||
/* FIXME: a transient duplicate would be a reason
|
||||
* to select the old instead of the new. */
|
||||
log_warn("Found duplicate PV %s: using %s not %s",
|
||||
pvid_s,
|
||||
dev_name(dev),
|
||||
dev_name(existing->dev));
|
||||
log_warn("Using duplicate PV %s which is last seen, replacing %s",
|
||||
dev_name(dev),
|
||||
dev_name(existing->dev));
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The new dev is the same as the existing dev.
|
||||
*
|
||||
* FIXME: Why can't we just return NULL here if the
|
||||
* device already exists? Things don't seem to work
|
||||
* if we do that for some reason.
|
||||
*/
|
||||
log_verbose("Found same device %s with same pvid %s",
|
||||
dev_name(existing->dev), pvid_s);
|
||||
}
|
||||
if (strcmp(pvid_s, existing->dev->pvid))
|
||||
log_debug_cache("Updating pvid cache to %s (%s) from %s (%s)",
|
||||
pvid_s, dev_name(dev),
|
||||
existing->dev->pvid, dev_name(existing->dev));
|
||||
/* Switch over to new preferred device */
|
||||
|
||||
/*
|
||||
* This happens when running pvcreate on an existing PV.
|
||||
*/
|
||||
if (strcmp(pvid_s, existing->dev->pvid)) {
|
||||
log_verbose("Replacing dev %s pvid %s with dev %s pvid %s",
|
||||
dev_name(existing->dev), existing->dev->pvid,
|
||||
dev_name(dev), pvid_s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch over to new preferred device.
|
||||
*/
|
||||
existing->dev = dev;
|
||||
info = existing;
|
||||
/* Has labeller changed? */
|
||||
@@ -2073,7 +2336,8 @@ int lvmcache_lookup_mda(struct lvmcache_vgsummary *vgsummary)
|
||||
vgsummary->vgname = vginfo->vgname;
|
||||
vgsummary->creation_host = vginfo->creation_host;
|
||||
vgsummary->vgstatus = vginfo->status;
|
||||
memcpy((char *)&vgsummary->vgid, vginfo->vgid, sizeof(vginfo->vgid));
|
||||
/* vginfo->vgid has 1 extra byte then vgsummary->vgid */
|
||||
memcpy(&vgsummary->vgid, vginfo->vgid, sizeof(vgsummary->vgid));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -2081,3 +2345,16 @@ int lvmcache_lookup_mda(struct lvmcache_vgsummary *vgsummary)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lvmcache_contains_lock_type_sanlock(struct cmd_context *cmd)
|
||||
{
|
||||
struct lvmcache_vginfo *vginfo;
|
||||
|
||||
dm_list_iterate_items(vginfo, &_vginfos) {
|
||||
if (vginfo->lock_type && !strcmp(vginfo->lock_type, "sanlock"))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
19
lib/cache/lvmcache.h
vendored
19
lib/cache/lvmcache.h
vendored
@@ -39,11 +39,23 @@ struct disk_locn;
|
||||
|
||||
struct lvmcache_vginfo;
|
||||
|
||||
/*
|
||||
* vgsummary represents a summary of the VG that is read
|
||||
* without a lock. The info does not come through vg_read(),
|
||||
* but through reading mdas. It provides information about
|
||||
* the VG that is needed to lock the VG and then read it fully
|
||||
* with vg_read(), after which the VG summary should be checked
|
||||
* against the full VG metadata to verify it was correct (since
|
||||
* it was read without a lock.)
|
||||
*
|
||||
* Once read, vgsummary information is saved in lvmcache_vginfo.
|
||||
*/
|
||||
struct lvmcache_vgsummary {
|
||||
const char *vgname;
|
||||
struct id vgid;
|
||||
uint64_t vgstatus;
|
||||
char *creation_host;
|
||||
const char *lock_type;
|
||||
uint32_t mda_checksum;
|
||||
size_t mda_size;
|
||||
};
|
||||
@@ -107,6 +119,9 @@ struct dm_list *lvmcache_get_vgnames(struct cmd_context *cmd,
|
||||
struct dm_list *lvmcache_get_vgids(struct cmd_context *cmd,
|
||||
int include_internal);
|
||||
|
||||
int lvmcache_get_vgnameids(struct cmd_context *cmd, int include_internal,
|
||||
struct dm_list *vgnameids);
|
||||
|
||||
/* Returns list of struct dm_str_list containing pool-allocated copy of pvids */
|
||||
struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
|
||||
const char *vgid);
|
||||
@@ -171,4 +186,8 @@ void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv,
|
||||
|
||||
int lvmcache_found_duplicate_pvs(void);
|
||||
|
||||
void lvmcache_set_preferred_duplicates(const char *vgid);
|
||||
|
||||
int lvmcache_contains_lock_type_sanlock(struct cmd_context *cmd);
|
||||
|
||||
#endif
|
||||
|
||||
637
lib/cache/lvmetad.c
vendored
637
lib/cache/lvmetad.c
vendored
@@ -22,6 +22,7 @@
|
||||
#include "format-text.h" // TODO for disk_locn, used as a DA representation
|
||||
#include "crc.h"
|
||||
#include "lvm-signal.h"
|
||||
#include "lvmlockd.h"
|
||||
|
||||
#define SCAN_TIMEOUT_SECONDS 80
|
||||
#define MAX_RESCANS 10 /* Maximum number of times to scan all PVs and retry if the daemon returns a token mismatch error */
|
||||
@@ -34,12 +35,13 @@ static char *_lvmetad_token = NULL;
|
||||
static const char *_lvmetad_socket = NULL;
|
||||
static struct cmd_context *_lvmetad_cmd = NULL;
|
||||
|
||||
static struct volume_group *lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg);
|
||||
|
||||
void lvmetad_disconnect(void)
|
||||
{
|
||||
if (_lvmetad_connected)
|
||||
daemon_close(_lvmetad);
|
||||
_lvmetad_connected = 0;
|
||||
_lvmetad_cmd = NULL;
|
||||
}
|
||||
|
||||
void lvmetad_init(struct cmd_context *cmd)
|
||||
@@ -47,6 +49,10 @@ void lvmetad_init(struct cmd_context *cmd)
|
||||
if (!_lvmetad_use && !access(getenv("LVM_LVMETAD_PIDFILE") ? : LVMETAD_PIDFILE, F_OK))
|
||||
log_warn("WARNING: lvmetad is running but disabled."
|
||||
" Restart lvmetad before enabling it!");
|
||||
|
||||
if (_lvmetad_connected)
|
||||
log_debug(INTERNAL_ERROR "Refreshing lvmetad global handle while connection with the daemon is active");
|
||||
|
||||
_lvmetad_cmd = cmd;
|
||||
}
|
||||
|
||||
@@ -142,7 +148,7 @@ static int _lvmetad_pvscan_all_devs(struct cmd_context *cmd, activation_handler
|
||||
static daemon_reply _lvmetad_send(const char *id, ...)
|
||||
{
|
||||
va_list ap;
|
||||
daemon_reply repl;
|
||||
daemon_reply repl = { 0 };
|
||||
daemon_request req;
|
||||
unsigned num_rescans = 0;
|
||||
unsigned total_usecs_waited = 0;
|
||||
@@ -152,8 +158,10 @@ static daemon_reply _lvmetad_send(const char *id, ...)
|
||||
retry:
|
||||
req = daemon_request_make(id);
|
||||
|
||||
if (_lvmetad_token)
|
||||
daemon_request_extend(req, "token = %s", _lvmetad_token, NULL);
|
||||
if (_lvmetad_token && !daemon_request_extend(req, "token = %s", _lvmetad_token, NULL)) {
|
||||
repl.error = ENOMEM;
|
||||
return repl;
|
||||
}
|
||||
|
||||
va_start(ap, id);
|
||||
daemon_request_extend_v(req, ap);
|
||||
@@ -265,11 +273,12 @@ static int _read_mda(struct lvmcache_info *info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lvmcache_info *_pv_populate_lvmcache(struct cmd_context *cmd,
|
||||
struct dm_config_node *cn,
|
||||
struct format_type *fmt, dev_t fallback)
|
||||
static int _pv_populate_lvmcache(struct cmd_context *cmd,
|
||||
struct dm_config_node *cn,
|
||||
struct format_type *fmt, dev_t fallback)
|
||||
{
|
||||
struct device *dev, *dev_alternate;
|
||||
struct device *dev, *dev_alternate, *dev_alternate_cache = NULL;
|
||||
struct label *label;
|
||||
struct id pvid, vgid;
|
||||
char mda_id[32];
|
||||
char da_id[32];
|
||||
@@ -278,7 +287,7 @@ static struct lvmcache_info *_pv_populate_lvmcache(struct cmd_context *cmd,
|
||||
struct dm_config_node *alt_devices = dm_config_find_node(cn->child, "devices_alternate");
|
||||
struct dm_config_value *alt_device = NULL;
|
||||
uint64_t offset, size;
|
||||
struct lvmcache_info *info;
|
||||
struct lvmcache_info *info, *info_alternate;
|
||||
const char *pvid_txt = dm_config_find_str(cn->child, "id", NULL),
|
||||
*vgid_txt = dm_config_find_str(cn->child, "vgid", NULL),
|
||||
*vgname = dm_config_find_str(cn->child, "vgname", NULL),
|
||||
@@ -292,7 +301,7 @@ static struct lvmcache_info *_pv_populate_lvmcache(struct cmd_context *cmd,
|
||||
|
||||
if (!fmt) {
|
||||
log_error("PV %s not recognised. Is the device missing?", pvid_txt);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev = dev_cache_get_by_devt(devt, cmd->filter);
|
||||
@@ -301,17 +310,17 @@ static struct lvmcache_info *_pv_populate_lvmcache(struct cmd_context *cmd,
|
||||
|
||||
if (!dev) {
|
||||
log_warn("WARNING: Device for PV %s not found or rejected by a filter.", pvid_txt);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pvid_txt || !id_read_format(&pvid, pvid_txt)) {
|
||||
log_error("Missing or ill-formatted PVID for PV: %s.", pvid_txt);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vgid_txt) {
|
||||
if (!id_read_format(&vgid, vgid_txt))
|
||||
return_NULL;
|
||||
return_0;
|
||||
} else
|
||||
strcpy((char*)&vgid, fmt->orphan_vg_name);
|
||||
|
||||
@@ -320,7 +329,7 @@ static struct lvmcache_info *_pv_populate_lvmcache(struct cmd_context *cmd,
|
||||
|
||||
if (!(info = lvmcache_add(fmt->labeller, (const char *)&pvid, dev,
|
||||
vgname, (const char *)&vgid, 0)))
|
||||
return_NULL;
|
||||
return_0;
|
||||
|
||||
lvmcache_get_label(info)->sector = label_sector;
|
||||
lvmcache_get_label(info)->dev = dev;
|
||||
@@ -366,21 +375,54 @@ static struct lvmcache_info *_pv_populate_lvmcache(struct cmd_context *cmd,
|
||||
|
||||
while (alt_device) {
|
||||
dev_alternate = dev_cache_get_by_devt(alt_device->v.i, cmd->filter);
|
||||
if (dev_alternate)
|
||||
lvmcache_add(fmt->labeller, (const char *)&pvid, dev_alternate,
|
||||
vgname, (const char *)&vgid, 0);
|
||||
else
|
||||
if (dev_alternate) {
|
||||
if ((info_alternate = lvmcache_add(fmt->labeller, (const char *)&pvid, dev_alternate,
|
||||
vgname, (const char *)&vgid, 0))) {
|
||||
dev_alternate_cache = dev_alternate;
|
||||
info = info_alternate;
|
||||
lvmcache_get_label(info)->dev = dev_alternate;
|
||||
}
|
||||
} else {
|
||||
log_warn("Duplicate of PV %s dev %s exists on unknown device %"PRId64 ":%" PRId64,
|
||||
pvid_txt, dev_name(dev), MAJOR(alt_device->v.i), MINOR(alt_device->v.i));
|
||||
}
|
||||
alt_device = alt_device->next;
|
||||
}
|
||||
|
||||
return info;
|
||||
/*
|
||||
* Update lvmcache with the info about the alternate device by
|
||||
* reading its label, which should update lvmcache.
|
||||
*/
|
||||
if (dev_alternate_cache) {
|
||||
if (!label_read(dev_alternate_cache, &label, 0)) {
|
||||
log_warn("No PV label found on duplicate device %s.", dev_name(dev_alternate_cache));
|
||||
}
|
||||
}
|
||||
|
||||
lvmcache_set_preferred_duplicates((const char *)&vgid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _pv_update_struct_pv(struct physical_volume *pv, struct format_instance *fid)
|
||||
{
|
||||
struct lvmcache_info *info;
|
||||
if ((info = lvmcache_info_from_pvid((const char *)&pv->id, 0))) {
|
||||
pv->label_sector = lvmcache_get_label(info)->sector;
|
||||
pv->dev = lvmcache_device(info);
|
||||
if (!pv->dev)
|
||||
pv->status |= MISSING_PV;
|
||||
if (!lvmcache_fid_add_mdas_pv(info, fid))
|
||||
return_0;
|
||||
pv->fid = fid;
|
||||
} else
|
||||
pv->status |= MISSING_PV; /* probably missing */
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgname, const char *vgid)
|
||||
{
|
||||
struct volume_group *vg = NULL;
|
||||
struct volume_group *vg2 = NULL;
|
||||
daemon_reply reply;
|
||||
int found;
|
||||
char uuid[64];
|
||||
@@ -392,7 +434,6 @@ struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgna
|
||||
struct format_type *fmt;
|
||||
struct dm_config_node *pvcn;
|
||||
struct pv_list *pvl;
|
||||
struct lvmcache_info *info;
|
||||
|
||||
if (!lvmetad_active())
|
||||
return NULL;
|
||||
@@ -443,22 +484,38 @@ struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgna
|
||||
for (pvcn = pvcn->child; pvcn; pvcn = pvcn->sib)
|
||||
_pv_populate_lvmcache(cmd, pvcn, fmt, 0);
|
||||
|
||||
if ((pvcn = dm_config_find_node(top, "metadata/outdated_pvs")))
|
||||
for (pvcn = pvcn->child; pvcn; pvcn = pvcn->sib)
|
||||
_pv_populate_lvmcache(cmd, pvcn, fmt, 0);
|
||||
|
||||
top->key = name;
|
||||
if (!(vg = import_vg_from_config_tree(reply.cft, fid)))
|
||||
if (!(vg = import_vg_from_lvmetad_config_tree(reply.cft, fid)))
|
||||
goto_out;
|
||||
|
||||
/*
|
||||
* locking may have detected a newer vg version and
|
||||
* invalidated the cached vg.
|
||||
*/
|
||||
if (dm_config_find_node(reply.cft->root, "vg_invalid")) {
|
||||
log_debug_lvmetad("Update invalid lvmetad cache for VG %s", vgname);
|
||||
vg2 = lvmetad_pvscan_vg(cmd, vg);
|
||||
release_vg(vg);
|
||||
vg = vg2;
|
||||
fid = vg->fid;
|
||||
}
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if ((info = lvmcache_info_from_pvid((const char *)&pvl->pv->id, 0))) {
|
||||
pvl->pv->label_sector = lvmcache_get_label(info)->sector;
|
||||
pvl->pv->dev = lvmcache_device(info);
|
||||
if (!pvl->pv->dev)
|
||||
pvl->pv->status |= MISSING_PV;
|
||||
if (!lvmcache_fid_add_mdas_pv(info, fid)) {
|
||||
vg = NULL;
|
||||
goto_out; /* FIXME error path */
|
||||
}
|
||||
} else
|
||||
pvl->pv->status |= MISSING_PV; /* probably missing */
|
||||
if (!_pv_update_struct_pv(pvl->pv, fid)) {
|
||||
vg = NULL;
|
||||
goto_out; /* FIXME error path */
|
||||
}
|
||||
}
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs_outdated) {
|
||||
if (!_pv_update_struct_pv(pvl->pv, fid)) {
|
||||
vg = NULL;
|
||||
goto_out; /* FIXME error path */
|
||||
}
|
||||
}
|
||||
|
||||
lvmcache_update_vg(vg, 0);
|
||||
@@ -657,6 +714,56 @@ int lvmetad_pv_list_to_lvmcache(struct cmd_context *cmd)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lvmetad_get_vgnameids(struct cmd_context *cmd, struct dm_list *vgnameids)
|
||||
{
|
||||
struct vgnameid_list *vgnl;
|
||||
struct id vgid;
|
||||
const char *vgid_txt;
|
||||
const char *vg_name;
|
||||
daemon_reply reply;
|
||||
struct dm_config_node *cn;
|
||||
|
||||
log_debug_lvmetad("Asking lvmetad for complete list of known VG ids/names");
|
||||
reply = _lvmetad_send("vg_list", NULL);
|
||||
if (!_lvmetad_handle_reply(reply, "list VGs", "", NULL)) {
|
||||
daemon_reply_destroy(reply);
|
||||
return_0;
|
||||
}
|
||||
|
||||
if ((cn = dm_config_find_node(reply.cft->root, "volume_groups"))) {
|
||||
for (cn = cn->child; cn; cn = cn->sib) {
|
||||
vgid_txt = cn->key;
|
||||
if (!id_read_format(&vgid, vgid_txt)) {
|
||||
stack;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl)))) {
|
||||
log_error("vgnameid_list allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(vg_name = dm_config_find_str(cn->child, "name", NULL))) {
|
||||
log_error("vg_list no name found.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vgnl->vgid = dm_pool_strdup(cmd->mem, (char *)&vgid);
|
||||
vgnl->vg_name = dm_pool_strdup(cmd->mem, vg_name);
|
||||
|
||||
if (!vgnl->vgid || !vgnl->vg_name) {
|
||||
log_error("vgnameid_list member allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_list_add(vgnameids, &vgnl->list);
|
||||
}
|
||||
}
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lvmetad_vg_list_to_lvmcache(struct cmd_context *cmd)
|
||||
{
|
||||
struct volume_group *tmp;
|
||||
@@ -845,6 +952,51 @@ int lvmetad_pv_found(const struct id *pvid, struct device *dev, const struct for
|
||||
daemon_reply_int(reply, "seqno_after", -1) != daemon_reply_int(reply, "seqno_before", -1)))
|
||||
log_warn("WARNING: Inconsistent metadata found for VG %s", vg->name);
|
||||
|
||||
/*
|
||||
* pvscan --cache does not perform any lvmlockd locking, and
|
||||
* pvscan --cache -aay skips autoactivation in lockd VGs.
|
||||
*
|
||||
* pvscan --cache populates lvmetad with VG metadata from disk.
|
||||
* No lvmlockd locking is needed. It is expected that lockd VG
|
||||
* metadata that is read by pvscan and populated in lvmetad may
|
||||
* be immediately stale due to changes to the VG from other hosts
|
||||
* during or after this pvscan. This is normal and not a problem.
|
||||
* When a subsequent lvm command uses the VG, it will lock the VG
|
||||
* with lvmlockd, read the VG from lvmetad, and update the cached
|
||||
* copy from disk if necessary.
|
||||
*
|
||||
* pvscan --cache -aay does not activate LVs in lockd VGs because
|
||||
* activation requires locking, and a lock-start operation is needed
|
||||
* on a lockd VG before any locking can be performed in it.
|
||||
*
|
||||
* An equivalent of pvscan --cache -aay for lockd VGs is:
|
||||
* 1. pvscan --cache
|
||||
* 2. vgchange --lock-start
|
||||
* 3. vgchange -aay -S 'locktype=sanlock || locktype=dlm'
|
||||
*
|
||||
* [We could eventually add support for autoactivating lockd VGs
|
||||
* using pvscan by incorporating the lock start step (which can
|
||||
* take a long time), but there may be a better option than
|
||||
* continuing to overload pvscan.]
|
||||
*
|
||||
* Stages of starting a lockd VG:
|
||||
*
|
||||
* . pvscan --cache populates lockd VGs in lvmetad without locks,
|
||||
* and this initial cached copy may quickly become stale.
|
||||
*
|
||||
* . vgchange --lock-start VG reads the VG without the VG lock
|
||||
* because no locks are available until the locking is started.
|
||||
* It only uses the VG name and lock_type from the VG metadata,
|
||||
* and then only uses it to start the VG lockspace in lvmlockd.
|
||||
*
|
||||
* . Further lvm commands, e.g. activation, can then lock the VG
|
||||
* with lvmlockd and use current VG metdata.
|
||||
*/
|
||||
if (handler && vg && is_lockd_type(vg->lock_type)) {
|
||||
log_debug_lvmetad("Skip pvscan activation for lockd type VG %s", vg->name);
|
||||
handler = NULL;
|
||||
}
|
||||
|
||||
if (result && handler) {
|
||||
status = daemon_reply_str(reply, "status", "<missing>");
|
||||
vgname = daemon_reply_str(reply, "vgname", "<missing>");
|
||||
@@ -912,7 +1064,8 @@ static int _lvmetad_pvscan_single(struct metadata_area *mda, void *baton)
|
||||
struct _lvmetad_pvscan_baton *b = baton;
|
||||
struct volume_group *this;
|
||||
|
||||
this = mda_is_ignored(mda) ? NULL : mda->ops->vg_read(b->fid, "", mda, NULL, NULL, 1);
|
||||
if (!(this = mda_is_ignored(mda) ? NULL : mda->ops->vg_read(b->fid, "", mda, NULL, NULL, 1)))
|
||||
return 1;
|
||||
|
||||
/* FIXME Also ensure contents match etc. */
|
||||
if (!b->vg || this->seqno > b->vg->seqno)
|
||||
@@ -923,6 +1076,100 @@ static int _lvmetad_pvscan_single(struct metadata_area *mda, void *baton)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The lock manager may detect that the vg cached in lvmetad is out of date,
|
||||
* due to something like an lvcreate from another host.
|
||||
* This is limited to changes that only affect the vg (not global state like
|
||||
* orphan PVs), so we only need to reread mdas on the vg's existing pvs.
|
||||
*/
|
||||
|
||||
static struct volume_group *lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
struct volume_group *vg_ret = NULL;
|
||||
struct dm_config_tree *vgmeta_ret = NULL;
|
||||
struct dm_config_tree *vgmeta;
|
||||
struct pv_list *pvl;
|
||||
struct lvmcache_info *info;
|
||||
struct format_instance *fid;
|
||||
struct format_instance_ctx fic = { .type = 0 };
|
||||
struct _lvmetad_pvscan_baton baton;
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
/* missing pv */
|
||||
if (!pvl->pv->dev)
|
||||
continue;
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid((const char *)&pvl->pv->id, 0))) {
|
||||
log_error("Failed to find cached info for PV %s.", pv_dev_name(pvl->pv));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
baton.vg = NULL;
|
||||
baton.fid = lvmcache_fmt(info)->ops->create_instance(lvmcache_fmt(info), &fic);
|
||||
|
||||
if (!baton.fid)
|
||||
return NULL;
|
||||
|
||||
if (baton.fid->fmt->features & FMT_OBSOLETE) {
|
||||
log_error("WARNING: Ignoring obsolete format of metadata (%s) on device %s when using lvmetad",
|
||||
baton.fid->fmt->name, dev_name(pvl->pv->dev));
|
||||
lvmcache_fmt(info)->ops->destroy_instance(baton.fid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lvmcache_foreach_mda(info, _lvmetad_pvscan_single, &baton);
|
||||
|
||||
if (!baton.vg) {
|
||||
lvmcache_fmt(info)->ops->destroy_instance(baton.fid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(vgmeta = export_vg_to_config_tree(baton.vg))) {
|
||||
log_error("VG export to config tree failed");
|
||||
release_vg(baton.vg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!vgmeta_ret) {
|
||||
vgmeta_ret = vgmeta;
|
||||
} else {
|
||||
if (!compare_config(vgmeta_ret->root, vgmeta->root)) {
|
||||
log_error("VG metadata comparison failed");
|
||||
dm_config_destroy(vgmeta);
|
||||
dm_config_destroy(vgmeta_ret);
|
||||
release_vg(baton.vg);
|
||||
return NULL;
|
||||
}
|
||||
dm_config_destroy(vgmeta);
|
||||
}
|
||||
|
||||
release_vg(baton.vg);
|
||||
}
|
||||
|
||||
if (vgmeta_ret) {
|
||||
fid = lvmcache_fmt(info)->ops->create_instance(lvmcache_fmt(info), &fic);
|
||||
if (!(vg_ret = import_vg_from_config_tree(vgmeta_ret, fid))) {
|
||||
log_error("VG import from config tree failed");
|
||||
lvmcache_fmt(info)->ops->destroy_instance(fid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update lvmetad with the newly read version of the VG.
|
||||
* The "precommitted" name is a misnomer in this case,
|
||||
* but that is the field which lvmetad_vg_update() uses
|
||||
* to send the metadata cft to lvmetad.
|
||||
*/
|
||||
vg_ret->cft_precommitted = vgmeta_ret;
|
||||
if (!lvmetad_vg_update(vg_ret))
|
||||
log_error("Failed to update lvmetad with new VG meta");
|
||||
vg_ret->cft_precommitted = NULL;
|
||||
dm_config_destroy(vgmeta_ret);
|
||||
}
|
||||
out:
|
||||
return vg_ret;
|
||||
}
|
||||
|
||||
int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
activation_handler handler, int ignore_obsolete)
|
||||
{
|
||||
@@ -1072,3 +1319,327 @@ int lvmetad_pvscan_foreign_vgs(struct cmd_context *cmd, activation_handler handl
|
||||
{
|
||||
return _lvmetad_pvscan_all_devs(cmd, handler, 1);
|
||||
}
|
||||
|
||||
int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg)
|
||||
{
|
||||
char uuid[64];
|
||||
daemon_reply reply;
|
||||
int result;
|
||||
|
||||
if (!id_write_format(&vg->id, uuid, sizeof(uuid)))
|
||||
return_0;
|
||||
|
||||
reply = _lvmetad_send("vg_clear_outdated_pvs", "vgid = %s", uuid, NULL);
|
||||
result = _lvmetad_handle_reply(reply, "clear the list of outdated PVs", vg->name, NULL);
|
||||
daemon_reply_destroy(reply);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Records the state of cached PVs in lvmetad so we can look for changes
|
||||
* after rescanning.
|
||||
*/
|
||||
struct pv_cache_list {
|
||||
struct dm_list list;
|
||||
dev_t devt;
|
||||
struct id pvid;
|
||||
const char *vgid;
|
||||
unsigned found : 1;
|
||||
unsigned update_udev : 1;
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the list of PVs known to lvmetad.
|
||||
*/
|
||||
static int _lvmetad_get_pv_cache_list(struct cmd_context *cmd, struct dm_list *pvc_list)
|
||||
{
|
||||
daemon_reply reply;
|
||||
struct dm_config_node *cn;
|
||||
struct pv_cache_list *pvcl;
|
||||
const char *pvid_txt;
|
||||
const char *vgid;
|
||||
|
||||
if (!lvmetad_active())
|
||||
return 1;
|
||||
|
||||
log_debug_lvmetad("Asking lvmetad for complete list of known PVs");
|
||||
reply = _lvmetad_send("pv_list", NULL);
|
||||
if (!_lvmetad_handle_reply(reply, "list PVs", "", NULL)) {
|
||||
log_error("lvmetad message failed.");
|
||||
daemon_reply_destroy(reply);
|
||||
return_0;
|
||||
}
|
||||
|
||||
if ((cn = dm_config_find_node(reply.cft->root, "physical_volumes"))) {
|
||||
for (cn = cn->child; cn; cn = cn->sib) {
|
||||
if (!(pvcl = dm_pool_zalloc(cmd->mem, sizeof(*pvcl)))) {
|
||||
log_error("pv_cache_list allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pvid_txt = cn->key;
|
||||
if (!id_read_format(&pvcl->pvid, pvid_txt)) {
|
||||
stack;
|
||||
continue;
|
||||
}
|
||||
|
||||
pvcl->devt = dm_config_find_int(cn->child, "device", 0);
|
||||
|
||||
if ((vgid = dm_config_find_str(cn->child, "vgid", NULL)))
|
||||
pvcl->vgid = dm_pool_strdup(cmd->mem, vgid);
|
||||
|
||||
dm_list_add(pvc_list, &pvcl->list);
|
||||
}
|
||||
}
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Opening the device RDWR should trigger a udev db update.
|
||||
* FIXME: is there a better way to update the udev db than
|
||||
* doing an open/close of the device? - For example writing
|
||||
* "change" to /sys/block/<device>/uevent?
|
||||
*/
|
||||
static void _update_pv_in_udev(struct cmd_context *cmd, dev_t devt)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
log_debug_devs("device %d:%d open to update udev",
|
||||
(int)MAJOR(devt), (int)MINOR(devt));
|
||||
|
||||
if (!(dev = dev_cache_get_by_devt(devt, cmd->lvmetad_filter))) {
|
||||
log_error("_update_pv_in_udev no dev found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dev_open(dev)) {
|
||||
stack;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare before and after PV lists from before/after rescanning,
|
||||
* and update udev db for changes.
|
||||
*
|
||||
* For PVs that have changed pvid or vgid in lvmetad from rescanning,
|
||||
* there may be information in the udev database to update, so open
|
||||
* these devices to trigger a udev update.
|
||||
*
|
||||
* "before" refers to the list of pvs from lvmetad before rescanning
|
||||
* "after" refers to the list of pvs from lvmetad after rescanning
|
||||
*
|
||||
* Comparing both lists, we can see which PVs changed (pvid or vgid),
|
||||
* and trigger a udev db update for those.
|
||||
*/
|
||||
static void _update_changed_pvs_in_udev(struct cmd_context *cmd,
|
||||
struct dm_list *pvc_before,
|
||||
struct dm_list *pvc_after)
|
||||
{
|
||||
struct pv_cache_list *before;
|
||||
struct pv_cache_list *after;
|
||||
char id_before[ID_LEN + 1] __attribute__((aligned(8)));
|
||||
char id_after[ID_LEN + 1] __attribute__((aligned(8)));
|
||||
int found;
|
||||
|
||||
dm_list_iterate_items(before, pvc_before) {
|
||||
found = 0;
|
||||
|
||||
dm_list_iterate_items(after, pvc_after) {
|
||||
if (after->found)
|
||||
continue;
|
||||
|
||||
if (before->devt != after->devt)
|
||||
continue;
|
||||
|
||||
if (!id_equal(&before->pvid, &after->pvid)) {
|
||||
memset(id_before, 0, sizeof(id_before));
|
||||
memset(id_after, 0, sizeof(id_after));
|
||||
strncpy(&id_before[0], (char *) &before->pvid, sizeof(id_before) - 1);
|
||||
strncpy(&id_after[0], (char *) &after->pvid, sizeof(id_after) - 1);
|
||||
|
||||
log_debug_devs("device %d:%d changed pvid from %s to %s",
|
||||
(int)MAJOR(before->devt), (int)MINOR(before->devt),
|
||||
id_before, id_after);
|
||||
|
||||
before->update_udev = 1;
|
||||
|
||||
} else if ((before->vgid && !after->vgid) ||
|
||||
(after->vgid && !before->vgid) ||
|
||||
(before->vgid && after->vgid && strcmp(before->vgid, after->vgid))) {
|
||||
|
||||
log_debug_devs("device %d:%d changed vg from %s to %s",
|
||||
(int)MAJOR(before->devt), (int)MINOR(before->devt),
|
||||
before->vgid ?: "none", after->vgid ?: "none");
|
||||
|
||||
before->update_udev = 1;
|
||||
}
|
||||
|
||||
after->found = 1;
|
||||
before->found = 1;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
memset(id_before, 0, sizeof(id_before));
|
||||
strncpy(&id_before[0], (char *) &before->pvid, sizeof(id_before) - 1);
|
||||
|
||||
log_debug_devs("device %d:%d pvid %s vg %s is gone",
|
||||
(int)MAJOR(before->devt), (int)MINOR(before->devt),
|
||||
id_before, before->vgid ? before->vgid : "none");
|
||||
|
||||
before->update_udev = 1;
|
||||
}
|
||||
}
|
||||
|
||||
dm_list_iterate_items(before, pvc_before) {
|
||||
if (before->update_udev)
|
||||
_update_pv_in_udev(cmd, before->devt);
|
||||
}
|
||||
|
||||
dm_list_iterate_items(after, pvc_after) {
|
||||
if (after->update_udev)
|
||||
_update_pv_in_udev(cmd, after->devt);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Before this command was run, some external entity may have
|
||||
* invalidated lvmetad's cache of global information, e.g. lvmlockd.
|
||||
*
|
||||
* The global information includes things like a new VG, a
|
||||
* VG that was removed, the assignment of a PV to a VG;
|
||||
* any change that is not isolated within a single VG.
|
||||
*
|
||||
* The external entity, like a lock manager, would invalidate
|
||||
* the lvmetad global cache if it detected that the global
|
||||
* information had been changed on disk by something other
|
||||
* than a local lvm command, e.g. an lvm command on another
|
||||
* host with access to the same devices. (How it detects
|
||||
* the change is specific to lock manager or other entity.)
|
||||
*
|
||||
* The effect is that metadata on disk is newer than the metadata
|
||||
* in the local lvmetad daemon, and the local lvmetad's cache
|
||||
* should be updated from disk before this command uses it.
|
||||
*
|
||||
* So, using this function, a command checks if lvmetad's global
|
||||
* cache is valid. If so, it does nothing. If not, it rescans
|
||||
* devices to update the lvmetad cache, then it notifies lvmetad
|
||||
* that it's cache is valid again (consistent with what's on disk.)
|
||||
* This command can then go ahead and use the newly refreshed metadata.
|
||||
*
|
||||
* 1. Check if the lvmetad global cache is invalid.
|
||||
* 2. If so, reread metadata from all devices and update the lvmetad cache.
|
||||
* 3. Tell lvmetad that the global cache is now valid.
|
||||
*/
|
||||
|
||||
void lvmetad_validate_global_cache(struct cmd_context *cmd, int force)
|
||||
{
|
||||
struct dm_list pvc_before; /* pv_cache_list */
|
||||
struct dm_list pvc_after; /* pv_cache_list */
|
||||
daemon_reply reply;
|
||||
int global_invalid;
|
||||
|
||||
dm_list_init(&pvc_before);
|
||||
dm_list_init(&pvc_after);
|
||||
|
||||
if (!lvmlockd_use()) {
|
||||
log_error(INTERNAL_ERROR "validate global cache without lvmlockd");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lvmetad_used())
|
||||
return;
|
||||
|
||||
log_debug_lvmetad("Validating global lvmetad cache");
|
||||
|
||||
if (force)
|
||||
goto do_scan;
|
||||
|
||||
reply = daemon_send_simple(_lvmetad, "get_global_info",
|
||||
"token = %s", "skip",
|
||||
NULL);
|
||||
|
||||
if (reply.error) {
|
||||
log_error("lvmetad_validate_global_cache get_global_info error %d", reply.error);
|
||||
goto do_scan;
|
||||
}
|
||||
|
||||
if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) {
|
||||
log_error("lvmetad_validate_global_cache get_global_info not ok");
|
||||
goto do_scan;
|
||||
}
|
||||
|
||||
global_invalid = daemon_reply_int(reply, "global_invalid", -1);
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
|
||||
if (!global_invalid) {
|
||||
/* cache is valid */
|
||||
return;
|
||||
}
|
||||
|
||||
do_scan:
|
||||
/*
|
||||
* Save the current state of pvs from lvmetad so after devices are
|
||||
* scanned, we can compare to the new state to see if pvs changed.
|
||||
*/
|
||||
_lvmetad_get_pv_cache_list(cmd, &pvc_before);
|
||||
|
||||
/*
|
||||
* Update the local lvmetad cache so it correctly reflects any
|
||||
* changes made on remote hosts.
|
||||
*/
|
||||
lvmetad_pvscan_all_devs(cmd, NULL);
|
||||
|
||||
/*
|
||||
* Clear the global_invalid flag in lvmetad.
|
||||
* Subsequent local commands that read global state
|
||||
* from lvmetad will not see global_invalid until
|
||||
* another host makes another global change.
|
||||
*/
|
||||
reply = daemon_send_simple(_lvmetad, "set_global_info",
|
||||
"token = %s", "skip",
|
||||
"global_invalid = %d", 0,
|
||||
NULL);
|
||||
if (reply.error)
|
||||
log_error("lvmetad_validate_global_cache set_global_info error %d", reply.error);
|
||||
|
||||
if (strcmp(daemon_reply_str(reply, "response", ""), "OK"))
|
||||
log_error("lvmetad_validate_global_cache set_global_info not ok");
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
|
||||
/*
|
||||
* Populate this command's lvmcache structures from lvmetad.
|
||||
*/
|
||||
lvmcache_seed_infos_from_lvmetad(cmd);
|
||||
|
||||
/*
|
||||
* Update the local udev database to reflect PV changes from
|
||||
* other hosts.
|
||||
*
|
||||
* Compare the before and after PV lists, and if a PV's
|
||||
* pvid or vgid has changed, then open that device to trigger
|
||||
* a uevent to update the udev db.
|
||||
*
|
||||
* This has no direct benefit to lvm, but is just a best effort
|
||||
* attempt to keep the udev db updated and reflecting current
|
||||
* lvm information.
|
||||
*
|
||||
* FIXME: lvmcache_seed_infos_from_lvmetad() and _lvmetad_get_pv_cache_list()
|
||||
* each get pv_list from lvmetad, and they could share a single pv_list reply.
|
||||
*/
|
||||
if (!dm_list_empty(&pvc_before)) {
|
||||
_lvmetad_get_pv_cache_list(cmd, &pvc_after);
|
||||
_update_changed_pvs_in_udev(cmd, &pvc_before, &pvc_after);
|
||||
}
|
||||
}
|
||||
|
||||
24
lib/cache/lvmetad.h
vendored
24
lib/cache/lvmetad.h
vendored
@@ -29,8 +29,7 @@ typedef int (*activation_handler) (struct cmd_context *cmd,
|
||||
|
||||
#ifdef LVMETAD_SUPPORT
|
||||
/*
|
||||
* Initialise the communication with lvmetad. Normally called by
|
||||
* lvmcache_init. Sets up a global handle for our process.
|
||||
* Sets up a global handle for our process.
|
||||
*/
|
||||
void lvmetad_init(struct cmd_context *);
|
||||
|
||||
@@ -59,7 +58,9 @@ int lvmetad_socket_present(void);
|
||||
|
||||
/*
|
||||
* Check whether lvmetad is active (where active means both that it is running
|
||||
* and that we have a working connection with it).
|
||||
* and that we have a working connection with it). It opens new connection
|
||||
* with lvmetad in the process when lvmetad is supposed to be used and the
|
||||
* connection is not open yet.
|
||||
*/
|
||||
int lvmetad_active(void);
|
||||
|
||||
@@ -70,8 +71,9 @@ int lvmetad_active(void);
|
||||
void lvmetad_connect_or_warn(void);
|
||||
|
||||
/*
|
||||
* Drop connection to lvmetad. A subsequent lvmetad_init() will re-establish
|
||||
* the connection (possibly at a different socket path).
|
||||
* Drop connection to lvmetad. A subsequent lvmetad_connect_or_warn or
|
||||
* lvmetad_active will re-establish the connection (possibly at a
|
||||
* different socket path).
|
||||
*/
|
||||
void lvmetad_disconnect(void);
|
||||
|
||||
@@ -142,6 +144,12 @@ int lvmetad_pv_lookup_by_dev(struct cmd_context *cmd, struct device *dev, int *f
|
||||
*/
|
||||
int lvmetad_vg_list_to_lvmcache(struct cmd_context *cmd);
|
||||
|
||||
/*
|
||||
* Request a list of vgid/vgname pairs for all VGs known to lvmetad.
|
||||
* Does not do vg_lookup's on each VG, and does not populate lvmcache.
|
||||
*/
|
||||
int lvmetad_get_vgnameids(struct cmd_context *cmd, struct dm_list *vgnameids);
|
||||
|
||||
/*
|
||||
* Find a VG by its ID or its name in the lvmetad cache. Gives NULL if the VG is
|
||||
* not found.
|
||||
@@ -158,6 +166,9 @@ int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
int lvmetad_pvscan_all_devs(struct cmd_context *cmd, activation_handler handler);
|
||||
int lvmetad_pvscan_foreign_vgs(struct cmd_context *cmd, activation_handler handler);
|
||||
|
||||
int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg);
|
||||
void lvmetad_validate_global_cache(struct cmd_context *cmd, int force);
|
||||
|
||||
# else /* LVMETAD_SUPPORT */
|
||||
|
||||
# define lvmetad_init(cmd) do { } while (0)
|
||||
@@ -179,10 +190,13 @@ int lvmetad_pvscan_foreign_vgs(struct cmd_context *cmd, activation_handler handl
|
||||
# define lvmetad_pv_lookup(cmd, pvid, found) (0)
|
||||
# define lvmetad_pv_lookup_by_dev(cmd, dev, found) (0)
|
||||
# define lvmetad_vg_list_to_lvmcache(cmd) (1)
|
||||
# define lvmetad_get_vgnameids(cmd, vgnameids) do { } while (0)
|
||||
# define lvmetad_vg_lookup(cmd, vgname, vgid) (NULL)
|
||||
# define lvmetad_pvscan_single(cmd, dev, handler, ignore_obsolete) (0)
|
||||
# define lvmetad_pvscan_all_devs(cmd, handler) (0)
|
||||
# define lvmetad_pvscan_foreign_vgs(cmd, handler) (0)
|
||||
# define lvmetad_vg_clear_outdated_pvs(vg) (1)
|
||||
# define lvmetad_validate_global_cache(cmd, force) do { } while (0)
|
||||
|
||||
# endif /* LVMETAD_SUPPORT */
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
#include "lv_alloc.h"
|
||||
#include "defaults.h"
|
||||
|
||||
static const char _cache_module[] = "cache";
|
||||
|
||||
/* TODO: using static field here, maybe should be a part of segment_type */
|
||||
static unsigned _feature_mask;
|
||||
|
||||
#define SEG_LOG_ERROR(t, p...) \
|
||||
log_error(t " segment %s of logical volume %s.", ## p, \
|
||||
dm_config_parent_name(sn), seg->lv->name), 0;
|
||||
@@ -66,20 +71,16 @@ static int _cache_pool_text_import(struct lv_segment *seg,
|
||||
if (dm_config_has_node(sn, "cache_mode")) {
|
||||
if (!(str = dm_config_find_str(sn, "cache_mode", NULL)))
|
||||
return SEG_LOG_ERROR("cache_mode must be a string in");
|
||||
if (!set_cache_pool_feature(&seg->feature_flags, str))
|
||||
if (!cache_set_mode(seg, str))
|
||||
return SEG_LOG_ERROR("Unknown cache_mode in");
|
||||
} else
|
||||
/* When missed in metadata, it's an old stuff - use writethrough */
|
||||
seg->feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH;
|
||||
}
|
||||
|
||||
if (dm_config_has_node(sn, "policy")) {
|
||||
if (!(str = dm_config_find_str(sn, "policy", NULL)))
|
||||
return SEG_LOG_ERROR("policy must be a string in");
|
||||
if (!(seg->policy_name = dm_pool_strdup(mem, str)))
|
||||
return SEG_LOG_ERROR("Failed to duplicate policy in");
|
||||
} else
|
||||
/* Cannot use 'just' default, so pick one */
|
||||
seg->policy_name = DEFAULT_CACHE_POOL_POLICY; /* FIXME make configurable */
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in policy args:
|
||||
@@ -99,6 +100,9 @@ static int _cache_pool_text_import(struct lv_segment *seg,
|
||||
* If the policy is not present, default policy is used.
|
||||
*/
|
||||
if ((sn = dm_config_find_node(sn, "policy_settings"))) {
|
||||
if (!seg->policy_name)
|
||||
return SEG_LOG_ERROR("policy_settings must have a policy_name in");
|
||||
|
||||
if (sn->v)
|
||||
return SEG_LOG_ERROR("policy_settings must be a section in");
|
||||
|
||||
@@ -127,24 +131,33 @@ static int _cache_pool_text_export(const struct lv_segment *seg,
|
||||
{
|
||||
const char *cache_mode;
|
||||
|
||||
if (!(cache_mode = get_cache_pool_cachemode_name(seg)))
|
||||
return_0;
|
||||
|
||||
outf(f, "data = \"%s\"", seg_lv(seg, 0)->name);
|
||||
outf(f, "metadata = \"%s\"", seg->metadata_lv->name);
|
||||
outf(f, "chunk_size = %" PRIu32, seg->chunk_size);
|
||||
outf(f, "cache_mode = \"%s\"", cache_mode);
|
||||
|
||||
if (seg->policy_name)
|
||||
/*
|
||||
* Cache pool used by a cache LV holds data. Not ideal,
|
||||
* but not worth to break backward compatibility, by shifting
|
||||
* content to cache segment
|
||||
*/
|
||||
if (cache_mode_is_set(seg)) {
|
||||
if (!(cache_mode = get_cache_mode_name(seg)))
|
||||
return_0;
|
||||
outf(f, "cache_mode = \"%s\"", cache_mode);
|
||||
}
|
||||
|
||||
if (seg->policy_name) {
|
||||
outf(f, "policy = \"%s\"", seg->policy_name);
|
||||
|
||||
if (seg->policy_settings) {
|
||||
if (strcmp(seg->policy_settings->key, "policy_settings")) {
|
||||
log_error(INTERNAL_ERROR "Incorrect policy_settings tree, %s.",
|
||||
seg->policy_settings->key);
|
||||
return 0;
|
||||
if (seg->policy_settings) {
|
||||
if (strcmp(seg->policy_settings->key, "policy_settings")) {
|
||||
log_error(INTERNAL_ERROR "Incorrect policy_settings tree, %s.",
|
||||
seg->policy_settings->key);
|
||||
return 0;
|
||||
}
|
||||
if (seg->policy_settings->child)
|
||||
out_config_node(f, seg->policy_settings);
|
||||
}
|
||||
out_config_node(f, seg->policy_settings);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -157,12 +170,29 @@ static void _destroy(struct segment_type *segtype)
|
||||
|
||||
#ifdef DEVMAPPER_SUPPORT
|
||||
static int _target_present(struct cmd_context *cmd,
|
||||
const struct lv_segment *seg __attribute__((unused)),
|
||||
unsigned *attributes __attribute__((unused)))
|
||||
const struct lv_segment *seg __attribute__((unused)),
|
||||
unsigned *attributes __attribute__((unused)))
|
||||
{
|
||||
uint32_t maj, min, patchlevel;
|
||||
/* List of features with their kernel target version */
|
||||
static const struct feature {
|
||||
uint32_t maj;
|
||||
uint32_t min;
|
||||
unsigned cache_feature;
|
||||
const char feature[12];
|
||||
const char module[12]; /* check dm-%s */
|
||||
} _features[] = {
|
||||
{ 1, 3, CACHE_FEATURE_POLICY_MQ, "policy_mq", "cache-mq" },
|
||||
{ 1, 8, CACHE_FEATURE_POLICY_SMQ, "policy_smq", "cache-smq" },
|
||||
};
|
||||
static const char _lvmconf[] = "global/cache_disabled_features";
|
||||
static unsigned _attrs = 0;
|
||||
static int _cache_checked = 0;
|
||||
static int _cache_present = 0;
|
||||
uint32_t maj, min, patchlevel;
|
||||
unsigned i;
|
||||
const struct dm_config_node *cn;
|
||||
const struct dm_config_value *cv;
|
||||
const char *str;
|
||||
|
||||
if (!_cache_checked) {
|
||||
_cache_present = target_present(cmd, "cache", 1);
|
||||
@@ -176,11 +206,53 @@ static int _target_present(struct cmd_context *cmd,
|
||||
|
||||
if ((maj < 1) ||
|
||||
((maj == 1) && (min < 3))) {
|
||||
log_error("The cache kernel module is version %u.%u.%u."
|
||||
" Version 1.3.0+ is required.",
|
||||
_cache_present = 0;
|
||||
log_error("The cache kernel module is version %u.%u.%u. "
|
||||
"Version 1.3.0+ is required.",
|
||||
maj, min, patchlevel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_features); ++i) {
|
||||
if (((maj > _features[i].maj) ||
|
||||
(maj == _features[i].maj && min >= _features[i].min)) &&
|
||||
(!_features[i].module[0] || module_present(cmd, _features[i].module)))
|
||||
_attrs |= _features[i].cache_feature;
|
||||
else
|
||||
log_very_verbose("Target %s does not support %s.",
|
||||
_cache_module, _features[i].feature);
|
||||
}
|
||||
}
|
||||
|
||||
if (attributes) {
|
||||
if (!_feature_mask) {
|
||||
/* Support runtime lvm.conf changes, N.B. avoid 32 feature */
|
||||
if ((cn = find_config_tree_array(cmd, global_cache_disabled_features_CFG, NULL))) {
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
if (cv->type != DM_CFG_STRING) {
|
||||
log_error("Ignoring invalid string in config file %s.",
|
||||
_lvmconf);
|
||||
continue;
|
||||
}
|
||||
str = cv->v.str;
|
||||
if (!*str)
|
||||
continue;
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_features); ++i)
|
||||
if (strcasecmp(str, _features[i].feature) == 0)
|
||||
_feature_mask |= _features[i].cache_feature;
|
||||
}
|
||||
}
|
||||
|
||||
_feature_mask = ~_feature_mask;
|
||||
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_features); ++i)
|
||||
if ((_attrs & _features[i].cache_feature) &&
|
||||
!(_feature_mask & _features[i].cache_feature))
|
||||
log_very_verbose("Target %s %s support disabled by %s",
|
||||
_cache_module, _features[i].feature, _lvmconf);
|
||||
}
|
||||
*attributes = _attrs & _feature_mask;
|
||||
}
|
||||
|
||||
return _cache_present;
|
||||
@@ -282,9 +354,16 @@ static int _cache_add_target_line(struct dev_manager *dm,
|
||||
struct dm_tree_node *node, uint64_t len,
|
||||
uint32_t *pvmove_mirror_count __attribute__((unused)))
|
||||
{
|
||||
struct lv_segment *cache_pool_seg = first_seg(seg->pool_lv);
|
||||
struct lv_segment *cache_pool_seg;
|
||||
char *metadata_uuid, *data_uuid, *origin_uuid;
|
||||
|
||||
if (!seg->pool_lv || !seg_is_cache(seg)) {
|
||||
log_error(INTERNAL_ERROR "Passed segment is not cache.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cache_pool_seg = first_seg(seg->pool_lv);
|
||||
|
||||
if (!(metadata_uuid = build_dm_uuid(mem, cache_pool_seg->metadata_lv, NULL)))
|
||||
return_0;
|
||||
|
||||
@@ -299,7 +378,9 @@ static int _cache_add_target_line(struct dev_manager *dm,
|
||||
metadata_uuid,
|
||||
data_uuid,
|
||||
origin_uuid,
|
||||
seg->cleaner_policy ? "cleaner" : cache_pool_seg->policy_name,
|
||||
seg->cleaner_policy ? "cleaner" :
|
||||
/* undefined policy name -> likely an old "mq" */
|
||||
cache_pool_seg->policy_name ? : "mq",
|
||||
seg->cleaner_policy ? NULL : cache_pool_seg->policy_settings,
|
||||
cache_pool_seg->chunk_size))
|
||||
return_0;
|
||||
@@ -361,5 +442,8 @@ int init_cache_segtypes(struct cmd_context *cmd,
|
||||
return_0;
|
||||
log_very_verbose("Initialised segtype: %s", segtype->name);
|
||||
|
||||
/* Reset mask for recalc */
|
||||
_feature_mask = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "lvmcache.h"
|
||||
#include "lvmetad.h"
|
||||
#include "archiver.h"
|
||||
#include "lvmpolld-client.h"
|
||||
|
||||
#ifdef HAVE_LIBDL
|
||||
#include "sharedlib.h"
|
||||
@@ -244,8 +245,10 @@ static int _parse_debug_classes(struct cmd_context *cmd)
|
||||
const struct dm_config_value *cv;
|
||||
int debug_classes = 0;
|
||||
|
||||
if (!(cn = find_config_tree_node(cmd, log_debug_classes_CFG, NULL)))
|
||||
return DEFAULT_LOGGED_DEBUG_CLASSES;
|
||||
if (!(cn = find_config_tree_array(cmd, log_debug_classes_CFG, NULL))) {
|
||||
log_error(INTERNAL_ERROR "Unable to find configuration for log/debug_classes.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
if (cv->type != DM_CFG_STRING) {
|
||||
@@ -273,6 +276,8 @@ static int _parse_debug_classes(struct cmd_context *cmd)
|
||||
debug_classes |= LOG_CLASS_CACHE;
|
||||
else if (!strcasecmp(cv->v.str, "locking"))
|
||||
debug_classes |= LOG_CLASS_LOCKING;
|
||||
else if (!strcasecmp(cv->v.str, "lvmpolld"))
|
||||
debug_classes |= LOG_CLASS_LVMPOLLD;
|
||||
else
|
||||
log_verbose("Unrecognised value for log/debug_classes: %s", cv->v.str);
|
||||
}
|
||||
@@ -410,6 +415,57 @@ static int _check_config(struct cmd_context *cmd)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *_set_time_format(struct cmd_context *cmd)
|
||||
{
|
||||
/* Compared to strftime, we do not allow "newline" character - the %n in format. */
|
||||
static const char *allowed_format_chars = "aAbBcCdDeFGghHIjklmMpPrRsStTuUVwWxXyYzZ%";
|
||||
static const char *allowed_alternative_format_chars_e = "cCxXyY";
|
||||
static const char *allowed_alternative_format_chars_o = "deHImMSuUVwWy";
|
||||
static const char *chars_to_check;
|
||||
const char *tf = find_config_tree_str(cmd, report_time_format_CFG, NULL);
|
||||
const char *p_fmt;
|
||||
size_t i;
|
||||
char c;
|
||||
|
||||
if (!*tf) {
|
||||
log_error("Configured time format is empty string.");
|
||||
goto bad;
|
||||
} else {
|
||||
p_fmt = tf;
|
||||
while ((c = *p_fmt)) {
|
||||
if (c == '%') {
|
||||
c = *++p_fmt;
|
||||
if (c == 'E') {
|
||||
c = *++p_fmt;
|
||||
chars_to_check = allowed_alternative_format_chars_e;
|
||||
} else if (c == 'O') {
|
||||
c = *++p_fmt;
|
||||
chars_to_check = allowed_alternative_format_chars_o;
|
||||
} else
|
||||
chars_to_check = allowed_format_chars;
|
||||
|
||||
for (i = 0; chars_to_check[i]; i++) {
|
||||
if (c == chars_to_check[i])
|
||||
break;
|
||||
}
|
||||
if (!chars_to_check[i])
|
||||
goto_bad;
|
||||
}
|
||||
else if (isprint(c))
|
||||
p_fmt++;
|
||||
else {
|
||||
log_error("Configured time format contains non-printable characters.");
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tf;
|
||||
bad:
|
||||
log_error("Invalid time format \"%s\" supplied.", tf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int process_profilable_config(struct cmd_context *cmd)
|
||||
{
|
||||
if (!(cmd->default_settings.unit_factor =
|
||||
@@ -423,6 +479,8 @@ int process_profilable_config(struct cmd_context *cmd)
|
||||
cmd->report_binary_values_as_numeric = find_config_tree_bool(cmd, report_binary_values_as_numeric_CFG, NULL);
|
||||
cmd->default_settings.suffix = find_config_tree_bool(cmd, global_suffix_CFG, NULL);
|
||||
cmd->report_list_item_separator = find_config_tree_str(cmd, report_list_item_separator_CFG, NULL);
|
||||
if (!(cmd->time_format = _set_time_format(cmd)))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -474,7 +532,6 @@ static int _process_config(struct cmd_context *cmd)
|
||||
const struct dm_config_node *cn;
|
||||
const struct dm_config_value *cv;
|
||||
int64_t pv_min_kb;
|
||||
const char *lvmetad_socket;
|
||||
int udev_disabled = 0;
|
||||
char sysfs_dir[PATH_MAX];
|
||||
|
||||
@@ -595,7 +652,7 @@ static int _process_config(struct cmd_context *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
if ((cn = find_config_tree_node(cmd, activation_mlock_filter_CFG, NULL)))
|
||||
if ((cn = find_config_tree_array(cmd, activation_mlock_filter_CFG, NULL)))
|
||||
for (cv = cn->v; cv; cv = cv->next)
|
||||
if ((cv->type != DM_CFG_STRING) || !cv->v.str[0])
|
||||
log_error("Ignoring invalid activation/mlock_filter entry in config file");
|
||||
@@ -617,30 +674,6 @@ static int _process_config(struct cmd_context *cmd)
|
||||
init_detect_internal_vg_cache_corruption
|
||||
(find_config_tree_bool(cmd, global_detect_internal_vg_cache_corruption_CFG, NULL));
|
||||
|
||||
lvmetad_disconnect();
|
||||
|
||||
lvmetad_socket = getenv("LVM_LVMETAD_SOCKET");
|
||||
if (!lvmetad_socket)
|
||||
lvmetad_socket = DEFAULT_RUN_DIR "/lvmetad.socket";
|
||||
|
||||
/* TODO?
|
||||
lvmetad_socket = find_config_tree_str(cmd, "lvmetad/socket_path",
|
||||
DEFAULT_RUN_DIR "/lvmetad.socket");
|
||||
*/
|
||||
lvmetad_set_socket(lvmetad_socket);
|
||||
cn = find_config_tree_node(cmd, devices_global_filter_CFG, NULL);
|
||||
lvmetad_set_token(cn ? cn->v : NULL);
|
||||
|
||||
if (find_config_tree_int(cmd, global_locking_type_CFG, NULL) == 3 &&
|
||||
find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL)) {
|
||||
log_warn("WARNING: configuration setting use_lvmetad overridden to 0 due to locking_type 3. "
|
||||
"Clustered environment not supported by lvmetad yet.");
|
||||
lvmetad_set_active(NULL, 0);
|
||||
} else
|
||||
lvmetad_set_active(NULL, find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL));
|
||||
|
||||
lvmetad_init(cmd);
|
||||
|
||||
if (!_init_system_id(cmd))
|
||||
return_0;
|
||||
|
||||
@@ -950,15 +983,9 @@ static int _init_dev_cache(struct cmd_context *cmd)
|
||||
|
||||
init_obtain_device_list_from_udev(device_list_from_udev);
|
||||
|
||||
if (!(cn = find_config_tree_node(cmd, devices_scan_CFG, NULL))) {
|
||||
if (!dev_cache_add_dir("/dev")) {
|
||||
log_error("Failed to add /dev to internal "
|
||||
"device cache");
|
||||
return 0;
|
||||
}
|
||||
log_verbose("device/scan not in config file: "
|
||||
"Defaulting to /dev");
|
||||
return 1;
|
||||
if (!(cn = find_config_tree_array(cmd, devices_scan_CFG, NULL))) {
|
||||
log_error(INTERNAL_ERROR "Unable to find configuration for devices/scan.");
|
||||
return_0;
|
||||
}
|
||||
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
@@ -996,7 +1023,7 @@ static int _init_dev_cache(struct cmd_context *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
if (!(cn = find_config_tree_node(cmd, devices_loopfiles_CFG, NULL)))
|
||||
if (!(cn = find_config_tree_array(cmd, devices_loopfiles_CFG, NULL)))
|
||||
return 1;
|
||||
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
@@ -1134,7 +1161,7 @@ bad:
|
||||
* md component filter -> fw raid filter
|
||||
*
|
||||
*/
|
||||
static int _init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
|
||||
int init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
|
||||
{
|
||||
const char *dev_cache;
|
||||
struct dev_filter *filter = NULL, *filter_components[2] = {0};
|
||||
@@ -1142,6 +1169,11 @@ static int _init_filters(struct cmd_context *cmd, unsigned load_persistent_cache
|
||||
const struct dm_config_node *cn;
|
||||
struct timespec ts, cts;
|
||||
|
||||
if (!cmd->initialized.connections) {
|
||||
log_error(INTERNAL_ERROR "connections must be initialized before filters");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd->dump_filter = 0;
|
||||
|
||||
cmd->lvmetad_filter = _init_lvmetad_filter_chain(cmd);
|
||||
@@ -1171,7 +1203,7 @@ static int _init_filters(struct cmd_context *cmd, unsigned load_persistent_cache
|
||||
}
|
||||
|
||||
/* filter component 1 */
|
||||
if ((cn = find_config_tree_node(cmd, devices_filter_CFG, NULL))) {
|
||||
if ((cn = find_config_tree_array(cmd, devices_filter_CFG, NULL))) {
|
||||
if (!(filter_components[1] = regex_filter_create(cn->v)))
|
||||
goto_bad;
|
||||
/* we have two filter components - create composite filter */
|
||||
@@ -1222,6 +1254,7 @@ static int _init_filters(struct cmd_context *cmd, unsigned load_persistent_cache
|
||||
dev_cache);
|
||||
}
|
||||
|
||||
cmd->initialized.filters = 1;
|
||||
return 1;
|
||||
bad:
|
||||
if (!filter) {
|
||||
@@ -1245,6 +1278,7 @@ bad:
|
||||
if (cmd->lvmetad_filter)
|
||||
cmd->lvmetad_filter->destroy(cmd->lvmetad_filter);
|
||||
|
||||
cmd->initialized.filters = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1288,7 +1322,7 @@ static int _init_formats(struct cmd_context *cmd)
|
||||
#ifdef HAVE_LIBDL
|
||||
/* Load any formats in shared libs if not static */
|
||||
if (!is_static() &&
|
||||
(cn = find_config_tree_node(cmd, global_format_libraries_CFG, NULL))) {
|
||||
(cn = find_config_tree_array(cmd, global_format_libraries_CFG, NULL))) {
|
||||
|
||||
const struct dm_config_value *cv;
|
||||
struct format_type *(*init_format_fn) (struct cmd_context *);
|
||||
@@ -1454,7 +1488,7 @@ static int _init_segtypes(struct cmd_context *cmd)
|
||||
#ifdef HAVE_LIBDL
|
||||
/* Load any formats in shared libs unless static */
|
||||
if (!is_static() &&
|
||||
(cn = find_config_tree_node(cmd, global_segment_libraries_CFG, NULL))) {
|
||||
(cn = find_config_tree_array(cmd, global_segment_libraries_CFG, NULL))) {
|
||||
|
||||
const struct dm_config_value *cv;
|
||||
int (*init_multiple_segtypes_fn) (struct cmd_context *,
|
||||
@@ -1618,11 +1652,80 @@ static int _reopen_stream(FILE *stream, int fd, const char *mode, const char *na
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _init_lvmetad(struct cmd_context *cmd)
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
const char *lvmetad_socket;
|
||||
|
||||
lvmetad_disconnect();
|
||||
|
||||
lvmetad_socket = getenv("LVM_LVMETAD_SOCKET");
|
||||
if (!lvmetad_socket)
|
||||
lvmetad_socket = DEFAULT_RUN_DIR "/lvmetad.socket";
|
||||
|
||||
/* TODO?
|
||||
lvmetad_socket = find_config_tree_str(cmd, "lvmetad/socket_path",
|
||||
DEFAULT_RUN_DIR "/lvmetad.socket");
|
||||
*/
|
||||
|
||||
lvmetad_set_socket(lvmetad_socket);
|
||||
cn = find_config_tree_array(cmd, devices_global_filter_CFG, NULL);
|
||||
lvmetad_set_token(cn ? cn->v : NULL);
|
||||
|
||||
if (find_config_tree_int(cmd, global_locking_type_CFG, NULL) == 3 &&
|
||||
find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL)) {
|
||||
log_warn("WARNING: configuration setting use_lvmetad overridden to 0 due to locking_type 3. "
|
||||
"Clustered environment not supported by lvmetad yet.");
|
||||
lvmetad_set_active(NULL, 0);
|
||||
} else
|
||||
lvmetad_set_active(NULL, find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL));
|
||||
|
||||
lvmetad_init(cmd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _init_lvmpolld(struct cmd_context *cmd)
|
||||
{
|
||||
const char *lvmpolld_socket;
|
||||
|
||||
lvmpolld_disconnect();
|
||||
|
||||
lvmpolld_socket = getenv("LVM_LVMPOLLD_SOCKET");
|
||||
if (!lvmpolld_socket)
|
||||
lvmpolld_socket = DEFAULT_RUN_DIR "/lvmpolld.socket";
|
||||
lvmpolld_set_socket(lvmpolld_socket);
|
||||
|
||||
lvmpolld_set_active(find_config_tree_bool(cmd, global_use_lvmpolld_CFG, NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int init_connections(struct cmd_context *cmd)
|
||||
{
|
||||
|
||||
if (!_init_lvmetad(cmd)) {
|
||||
log_error("Failed to initialize lvmetad connection.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_init_lvmpolld(cmd)) {
|
||||
log_error("Failed to initialize lvmpolld connection.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
cmd->initialized.connections = 1;
|
||||
return 1;
|
||||
bad:
|
||||
cmd->initialized.connections = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Entry point */
|
||||
struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
const char *system_dir,
|
||||
unsigned set_buffering,
|
||||
unsigned threaded)
|
||||
unsigned threaded,
|
||||
unsigned set_connections,
|
||||
unsigned set_filters)
|
||||
{
|
||||
struct cmd_context *cmd;
|
||||
FILE *new_stream;
|
||||
@@ -1760,15 +1863,12 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
goto_out;
|
||||
|
||||
if (!(cmd->dev_types = create_dev_types(cmd->proc_dir,
|
||||
find_config_tree_node(cmd, devices_types_CFG, NULL))))
|
||||
find_config_tree_array(cmd, devices_types_CFG, NULL))))
|
||||
goto_out;
|
||||
|
||||
if (!_init_dev_cache(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!_init_filters(cmd, 1))
|
||||
goto_out;
|
||||
|
||||
memlock_init(cmd);
|
||||
|
||||
if (!_init_formats(cmd))
|
||||
@@ -1787,12 +1887,18 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
|
||||
_init_globals(cmd);
|
||||
|
||||
if (set_connections && !init_connections(cmd))
|
||||
return_0;
|
||||
|
||||
if (set_filters && !init_filters(cmd, 1))
|
||||
goto_out;
|
||||
|
||||
cmd->default_settings.cache_vgmetadata = 1;
|
||||
cmd->current_settings = cmd->default_settings;
|
||||
|
||||
cmd->config_initialized = 1;
|
||||
cmd->initialized.config = 1;
|
||||
out:
|
||||
if (!cmd->config_initialized) {
|
||||
if (!cmd->initialized.config) {
|
||||
destroy_toolcontext(cmd);
|
||||
cmd = NULL;
|
||||
}
|
||||
@@ -1864,14 +1970,19 @@ static void _destroy_filters(struct cmd_context *cmd)
|
||||
cmd->full_filter->destroy(cmd->full_filter);
|
||||
cmd->lvmetad_filter = cmd->filter = cmd->full_filter = NULL;
|
||||
}
|
||||
cmd->initialized.filters = 0;
|
||||
}
|
||||
|
||||
int refresh_filters(struct cmd_context *cmd)
|
||||
{
|
||||
int r, saved_ignore_suspended_devices = ignore_suspended_devices();
|
||||
|
||||
if (!cmd->initialized.filters)
|
||||
/* if filters not initialized, there's nothing to refresh */
|
||||
return 1;
|
||||
|
||||
_destroy_filters(cmd);
|
||||
if (!(r = _init_filters(cmd, 0)))
|
||||
if (!(r = init_filters(cmd, 0)))
|
||||
stack;
|
||||
|
||||
/*
|
||||
@@ -1900,7 +2011,6 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
label_exit();
|
||||
_destroy_segtypes(&cmd->segtypes);
|
||||
_destroy_formats(cmd, &cmd->formats);
|
||||
_destroy_filters(cmd);
|
||||
|
||||
if (!dev_cache_exit())
|
||||
stack;
|
||||
@@ -1918,7 +2028,7 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
|
||||
_destroy_config(cmd);
|
||||
|
||||
cmd->config_initialized = 0;
|
||||
cmd->initialized.config = 0;
|
||||
|
||||
cmd->hosttags = 0;
|
||||
|
||||
@@ -1975,15 +2085,12 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
return_0;
|
||||
|
||||
if (!(cmd->dev_types = create_dev_types(cmd->proc_dir,
|
||||
find_config_tree_node(cmd, devices_types_CFG, NULL))))
|
||||
find_config_tree_array(cmd, devices_types_CFG, NULL))))
|
||||
return_0;
|
||||
|
||||
if (!_init_dev_cache(cmd))
|
||||
return_0;
|
||||
|
||||
if (!_init_filters(cmd, 0))
|
||||
return_0;
|
||||
|
||||
if (!_init_formats(cmd))
|
||||
return_0;
|
||||
|
||||
@@ -1996,7 +2103,13 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
if (!_init_backup(cmd))
|
||||
return_0;
|
||||
|
||||
cmd->config_initialized = 1;
|
||||
cmd->initialized.config = 1;
|
||||
|
||||
if (cmd->initialized.connections && !init_connections(cmd))
|
||||
return_0;
|
||||
|
||||
if (!refresh_filters(cmd))
|
||||
return_0;
|
||||
|
||||
reset_lvm_errno(1);
|
||||
return 1;
|
||||
@@ -2065,6 +2178,7 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
|
||||
lvmetad_release_token();
|
||||
lvmetad_disconnect();
|
||||
lvmpolld_disconnect();
|
||||
|
||||
release_log_memory();
|
||||
activation_exit();
|
||||
|
||||
@@ -60,29 +60,59 @@ struct config_tree_list {
|
||||
struct dm_config_tree *cft;
|
||||
};
|
||||
|
||||
struct cmd_context_initialized_parts {
|
||||
unsigned config:1; /* used to reinitialize config if previous init was not successful */
|
||||
unsigned filters:1;
|
||||
unsigned connections:1;
|
||||
};
|
||||
|
||||
/* FIXME Split into tool & library contexts */
|
||||
/* command-instance-related variables needed by library */
|
||||
struct cmd_context {
|
||||
struct dm_pool *libmem; /* For permanent config data */
|
||||
struct dm_pool *mem; /* Transient: Cleared between each command */
|
||||
/*
|
||||
* Memory handlers.
|
||||
*/
|
||||
struct dm_pool *libmem; /* for permanent config data */
|
||||
struct dm_pool *mem; /* transient: cleared between each command */
|
||||
|
||||
const struct format_type *fmt; /* Current format to use by default */
|
||||
struct format_type *fmt_backup; /* Format to use for backups */
|
||||
|
||||
struct dm_list formats; /* Available formats */
|
||||
struct dm_list segtypes; /* Available segment types */
|
||||
const char *system_id;
|
||||
const char *hostname;
|
||||
const char *kernel_vsn;
|
||||
|
||||
unsigned rand_seed;
|
||||
char *linebuffer;
|
||||
/*
|
||||
* Command line and arguments.
|
||||
*/
|
||||
const char *cmd_line;
|
||||
struct command *command;
|
||||
char **argv;
|
||||
struct arg_values *arg_values;
|
||||
struct dm_list arg_value_groups;
|
||||
unsigned is_long_lived:1; /* Optimises persistent_filter handling */
|
||||
|
||||
/*
|
||||
* Format handlers.
|
||||
*/
|
||||
const struct format_type *fmt; /* current format to use by default */
|
||||
struct format_type *fmt_backup; /* format to use for backups */
|
||||
struct dm_list formats; /* available formats */
|
||||
struct dm_list segtypes; /* available segment types */
|
||||
|
||||
/*
|
||||
* Machine and system identification.
|
||||
*/
|
||||
const char *system_id;
|
||||
const char *hostname;
|
||||
const char *kernel_vsn;
|
||||
|
||||
/*
|
||||
* Device identification.
|
||||
*/
|
||||
struct dev_types *dev_types; /* recognized extra device types. */
|
||||
|
||||
/*
|
||||
* Initialization state.
|
||||
*/
|
||||
struct cmd_context_initialized_parts initialized;
|
||||
|
||||
/*
|
||||
* Switches.
|
||||
*/
|
||||
unsigned is_long_lived:1; /* optimises persistent_filter handling */
|
||||
unsigned handles_missing_pvs:1;
|
||||
unsigned handles_unknown_segments:1;
|
||||
unsigned use_linear_target:1;
|
||||
@@ -93,59 +123,72 @@ struct cmd_context {
|
||||
unsigned report_binary_values_as_numeric:1;
|
||||
unsigned metadata_read_only:1;
|
||||
unsigned ignore_clustered_vgs:1;
|
||||
unsigned threaded:1; /* Set if running within a thread e.g. clvmd */
|
||||
|
||||
unsigned independent_metadata_areas:1; /* Active formats have MDAs outside PVs */
|
||||
unsigned threaded:1; /* set if running within a thread e.g. clvmd */
|
||||
unsigned independent_metadata_areas:1; /* active formats have MDAs outside PVs */
|
||||
unsigned unknown_system_id:1;
|
||||
unsigned include_foreign_vgs:1;
|
||||
unsigned include_active_foreign_vgs:1;
|
||||
unsigned error_foreign_vgs:1;
|
||||
|
||||
struct dev_types *dev_types;
|
||||
unsigned include_foreign_vgs:1; /* report/display cmds can reveal foreign VGs */
|
||||
unsigned include_shared_vgs:1; /* report/display cmds can reveal lockd VGs */
|
||||
unsigned include_active_foreign_vgs:1; /* cmd should process foreign VGs with active LVs */
|
||||
unsigned vg_read_print_access_error:1; /* print access errors from vg_read */
|
||||
unsigned lockd_gl_disable:1;
|
||||
unsigned lockd_vg_disable:1;
|
||||
unsigned lockd_lv_disable:1;
|
||||
unsigned lockd_gl_removed:1;
|
||||
unsigned lockd_vg_default_sh:1;
|
||||
unsigned lockd_vg_enforce_sh:1;
|
||||
|
||||
/*
|
||||
* Use of filters depends on whether lvmetad is used or not:
|
||||
*
|
||||
* - if lvmetad is used:
|
||||
* - cmd->lvmetad_filter used when scanning devices for lvmetad
|
||||
* - cmd->filter used when processing lvmetad responses
|
||||
* - cmd->full_filter used for remaining situations
|
||||
*
|
||||
* - if lvmetad is not used:
|
||||
* - cmd->lvmetad_filter is NULL
|
||||
* - cmd->filter == cmd->full_filter used for all situations
|
||||
*
|
||||
* Filtering.
|
||||
*/
|
||||
struct dev_filter *lvmetad_filter;
|
||||
struct dev_filter *filter;
|
||||
struct dev_filter *full_filter;
|
||||
int dump_filter; /* Dump filter when exiting? */
|
||||
struct dev_filter *lvmetad_filter; /* pre-lvmetad filter chain */
|
||||
struct dev_filter *filter; /* post-lvmetad filter chain */
|
||||
struct dev_filter *full_filter; /* lvmetad_filter + filter */
|
||||
int dump_filter; /* Dump filter when exiting? */
|
||||
|
||||
struct dm_list config_files; /* master lvm config + any existing tag configs */
|
||||
struct profile_params *profile_params; /* profile handling params including loaded profile configs */
|
||||
struct dm_config_tree *cft; /* the whole cascade: CONFIG_STRING -> CONFIG_PROFILE -> CONFIG_FILE/CONFIG_MERGED_FILES */
|
||||
int config_initialized; /* used to reinitialize config if previous init was not successful */
|
||||
|
||||
struct dm_hash_table *cft_def_hash; /* config definition hash used for validity check (item type + item recognized) */
|
||||
|
||||
/* selected settings with original default/configured value which can be changed during cmd processing */
|
||||
struct config_info default_settings;
|
||||
/* may contain changed values compared to default_settings */
|
||||
struct config_info current_settings;
|
||||
/*
|
||||
* Configuration.
|
||||
*/
|
||||
struct dm_list config_files; /* master lvm config + any existing tag configs */
|
||||
struct profile_params *profile_params; /* profile handling params including loaded profile configs */
|
||||
struct dm_config_tree *cft; /* the whole cascade: CONFIG_STRING -> CONFIG_PROFILE -> CONFIG_FILE/CONFIG_MERGED_FILES */
|
||||
struct dm_hash_table *cft_def_hash; /* config definition hash used for validity check (item type + item recognized) */
|
||||
struct config_info default_settings; /* selected settings with original default/configured value which can be changed during cmd processing */
|
||||
struct config_info current_settings; /* may contain changed values compared to default_settings */
|
||||
|
||||
/*
|
||||
* Archives and backups.
|
||||
*/
|
||||
struct archive_params *archive_params;
|
||||
struct backup_params *backup_params;
|
||||
const char *stripe_filler;
|
||||
|
||||
/* List of defined tags */
|
||||
struct dm_list tags;
|
||||
const char *report_list_item_separator;
|
||||
/*
|
||||
* Host tags.
|
||||
*/
|
||||
struct dm_list tags; /* list of defined tags */
|
||||
int hosttags;
|
||||
|
||||
const char *lib_dir; /* Cache value global/library_dir */
|
||||
/*
|
||||
* Paths.
|
||||
*/
|
||||
const char *lib_dir; /* cache value global/library_dir */
|
||||
char system_dir[PATH_MAX];
|
||||
char dev_dir[PATH_MAX];
|
||||
char proc_dir[PATH_MAX];
|
||||
|
||||
/*
|
||||
* Buffers.
|
||||
*/
|
||||
char display_buffer[NAME_LEN * 10]; /* ring buffer for upto 10 longest vg/lv names */
|
||||
unsigned display_lvname_idx; /* index to ring buffer */
|
||||
char *linebuffer;
|
||||
|
||||
/*
|
||||
* Others - unsorted.
|
||||
*/
|
||||
const char *report_list_item_separator;
|
||||
const char *time_format;
|
||||
unsigned rand_seed;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -155,13 +198,17 @@ struct cmd_context {
|
||||
struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
const char *system_dir,
|
||||
unsigned set_buffering,
|
||||
unsigned threaded);
|
||||
unsigned threaded,
|
||||
unsigned set_connections,
|
||||
unsigned set_filters);
|
||||
void destroy_toolcontext(struct cmd_context *cmd);
|
||||
int refresh_toolcontext(struct cmd_context *cmd);
|
||||
int refresh_filters(struct cmd_context *cmd);
|
||||
int process_profilable_config(struct cmd_context *cmd);
|
||||
int config_files_changed(struct cmd_context *cmd);
|
||||
int init_lvmcache_orphans(struct cmd_context *cmd);
|
||||
int init_filters(struct cmd_context *cmd, unsigned load_persistent_cache);
|
||||
int init_connections(struct cmd_context *cmd);
|
||||
|
||||
struct format_type *get_format_by_name(struct cmd_context *cmd, const char *format);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "toolcontext.h"
|
||||
#include "lvm-file.h"
|
||||
#include "memlock.h"
|
||||
#include "segtype.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -65,11 +66,11 @@ struct config_source {
|
||||
* Map each ID to respective definition of the configuration item.
|
||||
*/
|
||||
static struct cfg_def_item _cfg_def_items[CFG_COUNT + 1] = {
|
||||
#define cfg_section(id, name, parent, flags, since_version, unconfigured_path, comment) {id, parent, name, CFG_TYPE_SECTION, {0}, flags, since_version, unconfigured_path, comment},
|
||||
#define cfg(id, name, parent, flags, type, default_value, since_version, unconfigured_path, comment) {id, parent, name, type, {.v_##type = default_value}, flags, since_version, unconfigured_path, comment},
|
||||
#define cfg_runtime(id, name, parent, flags, type, since_version, unconfigured_path, comment) {id, parent, name, type, {.fn_##type = get_default_##id}, flags | CFG_DEFAULT_RUN_TIME, since_version, unconfigured_path, comment},
|
||||
#define cfg_array(id, name, parent, flags, types, default_value, since_version, unconfigured_path, comment) {id, parent, name, CFG_TYPE_ARRAY | types, {.v_CFG_TYPE_STRING = default_value}, flags, since_version, unconfigured_path, comment},
|
||||
#define cfg_array_runtime(id, name, parent, flags, types, since_version, unconfigured_path, comment) {id, parent, name, CFG_TYPE_ARRAY | types, {.fn_CFG_TYPE_STRING = get_default_##id}, flags | CFG_DEFAULT_RUN_TIME, since_version, unconfigured_path, comment},
|
||||
#define cfg_section(id, name, parent, flags, since_version, deprecated_since_version, deprecation_comment, comment) {id, parent, name, CFG_TYPE_SECTION, {0}, flags, since_version, {0}, deprecated_since_version, deprecation_comment, comment},
|
||||
#define cfg(id, name, parent, flags, type, default_value, since_version, unconfigured_value, deprecated_since_version, deprecation_comment, comment) {id, parent, name, type, {.v_##type = default_value}, flags, since_version, {.v_UNCONFIGURED = unconfigured_value}, deprecated_since_version, deprecation_comment, comment},
|
||||
#define cfg_runtime(id, name, parent, flags, type, since_version, deprecated_since_version, deprecation_comment, comment) {id, parent, name, type, {.fn_##type = get_default_##id}, flags | CFG_DEFAULT_RUN_TIME, since_version, {.fn_UNCONFIGURED = get_default_unconfigured_##id}, deprecated_since_version, deprecation_comment, comment},
|
||||
#define cfg_array(id, name, parent, flags, types, default_value, since_version, unconfigured_value, deprecated_since_version, deprecation_comment, comment) {id, parent, name, CFG_TYPE_ARRAY | types, {.v_CFG_TYPE_STRING = default_value}, flags, since_version, {.v_UNCONFIGURED = unconfigured_value}, deprecated_since_version, deprecation_comment, comment},
|
||||
#define cfg_array_runtime(id, name, parent, flags, types, since_version, deprecated_since_version, deprecation_comment, comment) {id, parent, name, CFG_TYPE_ARRAY | types, {.fn_CFG_TYPE_STRING = get_default_##id}, flags | CFG_DEFAULT_RUN_TIME, since_version, {.fn_UNCONFIGURED = get_default_unconfigured_##id}, deprecated_since_version, deprecation_comment, comment},
|
||||
#include "config_settings.h"
|
||||
#undef cfg_section
|
||||
#undef cfg
|
||||
@@ -604,6 +605,7 @@ struct timespec config_file_timestamp(struct dm_config_tree *cft)
|
||||
}
|
||||
|
||||
#define cfg_def_get_item_p(id) (&_cfg_def_items[id])
|
||||
#define cfg_def_get_default_unconfigured_value_hint(cmd,item) ((item->flags & CFG_DEFAULT_RUN_TIME) ? item->default_unconfigured_value.fn_UNCONFIGURED(cmd) : item->default_unconfigured_value.v_UNCONFIGURED)
|
||||
#define cfg_def_get_default_value_hint(cmd,item,type,profile) ((item->flags & CFG_DEFAULT_RUN_TIME) ? item->default_value.fn_##type(cmd,profile) : item->default_value.v_##type)
|
||||
#define cfg_def_get_default_value(cmd,item,type,profile) (item->flags & CFG_DEFAULT_UNDEFINED ? 0 : cfg_def_get_default_value_hint(cmd,item,type,profile))
|
||||
|
||||
@@ -665,22 +667,28 @@ static void _log_type_error(const char *path, cfg_def_type_t actual,
|
||||
actual_type_name, expected_type_name);
|
||||
}
|
||||
|
||||
static struct dm_config_value *_get_def_array_values(struct dm_config_tree *cft,
|
||||
const cfg_def_item_t *def)
|
||||
static struct dm_config_value *_get_def_array_values(struct cmd_context *cmd,
|
||||
struct dm_config_tree *cft,
|
||||
const cfg_def_item_t *def,
|
||||
uint32_t format_flags)
|
||||
{
|
||||
const char *def_enc_value;
|
||||
char *enc_value, *token, *p, *r;
|
||||
struct dm_config_value *array = NULL, *v = NULL, *oldv = NULL;
|
||||
|
||||
if (!def->default_value.v_CFG_TYPE_STRING) {
|
||||
def_enc_value = cfg_def_get_default_value(cmd, def, CFG_TYPE_ARRAY, NULL);
|
||||
|
||||
if (!def_enc_value) {
|
||||
if (!(array = dm_config_create_value(cft))) {
|
||||
log_error("Failed to create default empty array for %s.", def->name);
|
||||
return NULL;
|
||||
}
|
||||
array->type = DM_CFG_EMPTY_ARRAY;
|
||||
dm_config_value_set_format_flags(array, format_flags);
|
||||
return array;
|
||||
}
|
||||
|
||||
if (!(p = token = enc_value = dm_strdup(def->default_value.v_CFG_TYPE_STRING))) {
|
||||
if (!(p = token = enc_value = dm_strdup(def_enc_value))) {
|
||||
log_error("_get_def_array_values: dm_strdup failed");
|
||||
return NULL;
|
||||
}
|
||||
@@ -709,6 +717,9 @@ static struct dm_config_value *_get_def_array_values(struct dm_config_tree *cft,
|
||||
dm_free(enc_value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dm_config_value_set_format_flags(v, format_flags);
|
||||
|
||||
if (oldv)
|
||||
oldv->next = v;
|
||||
if (!array)
|
||||
@@ -825,6 +836,12 @@ static int _check_value_differs_from_default(struct cft_check_handle *handle,
|
||||
float f;
|
||||
const char *str;
|
||||
|
||||
if ((handle->ignoreunsupported && (def->flags & CFG_UNSUPPORTED)) ||
|
||||
(handle->ignoreadvanced && (def->flags & CFG_ADVANCED))) {
|
||||
diff = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if default value is undefined, the value used differs from default */
|
||||
if (def->flags & CFG_DEFAULT_UNDEFINED) {
|
||||
diff = 1;
|
||||
@@ -832,7 +849,7 @@ static int _check_value_differs_from_default(struct cft_check_handle *handle,
|
||||
}
|
||||
|
||||
if (!v_def && (def->type & CFG_TYPE_ARRAY)) {
|
||||
if (!(v_def_array = v_def_iter = _get_def_array_values(handle->cft, def)))
|
||||
if (!(v_def_array = v_def_iter = _get_def_array_values(handle->cmd, handle->cft, def, 0)))
|
||||
return_0;
|
||||
do {
|
||||
/* iterate over each element of the array and check its value */
|
||||
@@ -1024,9 +1041,14 @@ static int _config_def_check_tree(struct cft_check_handle *handle,
|
||||
size_t buf_size, struct dm_config_node *root)
|
||||
{
|
||||
struct dm_config_node *cn;
|
||||
cfg_def_item_t *def;
|
||||
int valid, r = 1;
|
||||
size_t len;
|
||||
|
||||
def = cfg_def_get_item_p(root->id);
|
||||
if (def->flags & CFG_SECTION_NO_CHECK)
|
||||
return 1;
|
||||
|
||||
for (cn = root->child; cn; cn = cn->sib) {
|
||||
if ((valid = _config_def_check_node(handle, vp, pvp, rp, prp,
|
||||
buf_size, cn)) && !cn->v) {
|
||||
@@ -1348,6 +1370,106 @@ int find_config_tree_bool(struct cmd_context *cmd, int id, struct profile *profi
|
||||
return b;
|
||||
}
|
||||
|
||||
static struct dm_config_node *_get_array_def_node(struct cmd_context *cmd,
|
||||
cfg_def_item_t *def,
|
||||
struct profile *profile)
|
||||
{
|
||||
struct dm_config_node *cn;
|
||||
|
||||
if (def->flags & CFG_DEFAULT_UNDEFINED)
|
||||
return NULL;
|
||||
|
||||
if (!(cn = dm_config_create_node(cmd->cft, def->name))) {
|
||||
log_error("Failed to create default array node for %s.", def->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(cn->v = _get_def_array_values(cmd, cmd->cft, def, 0))) {
|
||||
dm_pool_free(cmd->cft->mem, cn);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
struct _config_array_out_handle {
|
||||
struct dm_pool *mem;
|
||||
char *str;
|
||||
};
|
||||
|
||||
static int _config_array_line(const struct dm_config_node *cn, const char *line, void *baton)
|
||||
{
|
||||
struct _config_array_out_handle *handle = (struct _config_array_out_handle *) baton;
|
||||
|
||||
if (!(handle->str = dm_pool_strdup(handle->mem, line))) {
|
||||
log_error("_config_array_line: dm_pool_strdup failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _log_array_value_used(struct dm_pool *mem, const struct dm_config_node *cn,
|
||||
const char *path, int default_used)
|
||||
{
|
||||
struct _config_array_out_handle out_handle = { 0 };
|
||||
struct dm_config_node_out_spec out_spec = { 0 };
|
||||
uint32_t old_format_flags;
|
||||
|
||||
out_handle.mem = mem;
|
||||
out_spec.line_fn = _config_array_line;
|
||||
|
||||
old_format_flags = dm_config_value_get_format_flags(cn->v);
|
||||
dm_config_value_set_format_flags(cn->v,
|
||||
DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES |
|
||||
DM_CONFIG_VALUE_FMT_COMMON_ARRAY);
|
||||
|
||||
if (!dm_config_write_one_node_out(cn, &out_spec, &out_handle)) {
|
||||
log_error("_log_array_value_used: failed to write node value");
|
||||
out_handle.mem = NULL;
|
||||
}
|
||||
|
||||
if (default_used)
|
||||
log_very_verbose("%s not found in config: defaulting to %s",
|
||||
path, out_handle.mem ? out_handle.str : "<unknown>");
|
||||
else
|
||||
log_very_verbose("Setting %s to %s",
|
||||
path, out_handle.mem ? out_handle.str : "<unknown>");
|
||||
|
||||
if (out_handle.mem)
|
||||
dm_pool_free(out_handle.mem, out_handle.str);
|
||||
dm_config_value_set_format_flags(cn->v, old_format_flags);
|
||||
}
|
||||
|
||||
const struct dm_config_node *find_config_tree_array(struct cmd_context *cmd, int id, struct profile *profile)
|
||||
{
|
||||
cfg_def_item_t *item = cfg_def_get_item_p(id);
|
||||
char path[CFG_PATH_MAX_LEN];
|
||||
int profile_applied;
|
||||
const struct dm_config_node *cn = NULL, *cn_def = NULL;
|
||||
profile_applied = _apply_local_profile(cmd, profile);
|
||||
_cfg_def_make_path(path, sizeof(path), item->id, item, 0);
|
||||
|
||||
if (!(item->type & CFG_TYPE_ARRAY))
|
||||
log_error(INTERNAL_ERROR "%s cfg tree element not declared as array.", path);
|
||||
|
||||
if (_config_disabled(cmd, item, path) ||
|
||||
!(cn = find_config_tree_node(cmd, id, profile)))
|
||||
cn_def = _get_array_def_node(cmd, item, profile);
|
||||
|
||||
if (cn)
|
||||
_log_array_value_used(cmd->cft->mem, cn, path, 0);
|
||||
else if (cn_def) {
|
||||
_log_array_value_used(cmd->cft->mem, cn_def, path, 1);
|
||||
cn = cn_def;
|
||||
}
|
||||
|
||||
if (profile_applied)
|
||||
remove_config_tree_by_source(cmd, profile->source);
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
/* Insert cn2 after cn1 */
|
||||
static void _insert_config_node(struct dm_config_node **cn1,
|
||||
struct dm_config_node *cn2)
|
||||
@@ -1518,6 +1640,25 @@ static int _copy_one_line(const char *comment, char *line, int *pos, int len)
|
||||
return i;
|
||||
}
|
||||
|
||||
static int _get_config_node_version(uint16_t version_enc, char *version)
|
||||
{
|
||||
if (dm_snprintf(version, 9, "%u.%u.%u",
|
||||
(version_enc & 0xE000) >> 13,
|
||||
(version_enc & 0x1E00) >> 9,
|
||||
(version_enc & 0x1FF)) == -1) {
|
||||
log_error("_get_config_node_version: couldn't create version string");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _def_node_is_deprecated(cfg_def_item_t *def, struct config_def_tree_spec *spec)
|
||||
{
|
||||
return def->deprecated_since_version &&
|
||||
(spec->version >= def->deprecated_since_version);
|
||||
}
|
||||
|
||||
static int _out_prefix_fn(const struct dm_config_node *cn, const char *line, void *baton)
|
||||
{
|
||||
struct out_baton *out = baton;
|
||||
@@ -1527,13 +1668,11 @@ static int _out_prefix_fn(const struct dm_config_node *cn, const char *line, voi
|
||||
char path[CFG_PATH_MAX_LEN];
|
||||
char commentline[MAX_COMMENT_LINE+1];
|
||||
|
||||
if (cn->id < 0)
|
||||
if (cn->id <= 0)
|
||||
return 1;
|
||||
|
||||
if (!cn->id) {
|
||||
log_error(INTERNAL_ERROR "Configuration node %s has invalid id.", cn->key);
|
||||
return 0;
|
||||
}
|
||||
if (out->tree_spec->type == CFG_DEF_TREE_LIST)
|
||||
return 1;
|
||||
|
||||
if ((out->tree_spec->type == CFG_DEF_TREE_DIFF) &&
|
||||
(!(out->tree_spec->check_status[cn->id] & CFG_DIFF)))
|
||||
@@ -1541,21 +1680,28 @@ static int _out_prefix_fn(const struct dm_config_node *cn, const char *line, voi
|
||||
|
||||
cfg_def = cfg_def_get_item_p(cn->id);
|
||||
|
||||
if (out->tree_spec->withcomments || out->tree_spec->withfullcomments) {
|
||||
if (out->tree_spec->withsummary || out->tree_spec->withcomments) {
|
||||
_cfg_def_make_path(path, sizeof(path), cfg_def->id, cfg_def, 1);
|
||||
fprintf(out->fp, "\n");
|
||||
fprintf(out->fp, "%s# Configuration %s %s.\n", line, node_type_name, path);
|
||||
|
||||
if (out->tree_spec->withcomments &&
|
||||
_def_node_is_deprecated(cfg_def, out->tree_spec))
|
||||
fprintf(out->fp, "%s# %s", line, cfg_def->deprecation_comment);
|
||||
|
||||
if (cfg_def->comment) {
|
||||
int pos = 0;
|
||||
while (_copy_one_line(cfg_def->comment, commentline, &pos, strlen(cfg_def->comment))) {
|
||||
fprintf(out->fp, "%s# %s\n", line, commentline);
|
||||
/* withcomments prints only the first comment line. */
|
||||
if (!out->tree_spec->withfullcomments)
|
||||
/* withsummary prints only the first comment line. */
|
||||
if (!out->tree_spec->withcomments)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_def_node_is_deprecated(cfg_def, out->tree_spec))
|
||||
fprintf(out->fp, "%s# This configuration %s is deprecated.\n", line, node_type_name);
|
||||
|
||||
if (cfg_def->flags & CFG_ADVANCED)
|
||||
fprintf(out->fp, "%s# This configuration %s is advanced.\n", line, node_type_name);
|
||||
|
||||
@@ -1567,34 +1713,98 @@ static int _out_prefix_fn(const struct dm_config_node *cn, const char *line, voi
|
||||
|
||||
if (cfg_def->flags & CFG_DEFAULT_UNDEFINED)
|
||||
fprintf(out->fp, "%s# This configuration %s does not have a default value defined.\n", line, node_type_name);
|
||||
|
||||
if ((out->tree_spec->type == CFG_DEF_TREE_FULL) &&
|
||||
(out->tree_spec->check_status[cn->id] & CFG_USED))
|
||||
fprintf(out->fp, "%s# Value defined in existing configuration has been used for this setting.\n", line);
|
||||
}
|
||||
|
||||
if (out->tree_spec->withversions) {
|
||||
if (dm_snprintf(version, 9, "%u.%u.%u",
|
||||
(cfg_def->since_version & 0xE000) >> 13,
|
||||
(cfg_def->since_version & 0x1E00) >> 9,
|
||||
(cfg_def->since_version & 0x1FF)) == -1) {
|
||||
log_error("_out_prefix_fn: couldn't create version string");
|
||||
return 0;
|
||||
if (!_get_config_node_version(cfg_def->since_version, version))
|
||||
return_0;
|
||||
fprintf(out->fp, "%s# Available since version %s.\n", line, version);
|
||||
|
||||
if (_def_node_is_deprecated(cfg_def, out->tree_spec)) {
|
||||
if (!_get_config_node_version(cfg_def->deprecated_since_version, version))
|
||||
return_0;
|
||||
fprintf(out->fp, "%s# Deprecated since version %s.\n", line, version);
|
||||
}
|
||||
fprintf(out->fp, "%s# Since version %s.\n", line, version);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _should_print_cfg_with_undef_def_val(struct out_baton *out, cfg_def_item_t *cfg_def,
|
||||
const struct dm_config_node *cn)
|
||||
{
|
||||
if (!(cfg_def->flags & CFG_DEFAULT_UNDEFINED))
|
||||
return 1;
|
||||
|
||||
/* print it only if the value is directly defined in some config = it's used */
|
||||
return out->tree_spec->check_status && (out->tree_spec->check_status[cn->id] & CFG_USED);
|
||||
}
|
||||
|
||||
static int _out_line_fn(const struct dm_config_node *cn, const char *line, void *baton)
|
||||
{
|
||||
struct out_baton *out = baton;
|
||||
struct cfg_def_item *cfg_def = cfg_def_get_item_p(cn->id);
|
||||
struct cfg_def_item *cfg_def;
|
||||
char config_path[CFG_PATH_MAX_LEN];
|
||||
char summary[MAX_COMMENT_LINE+1];
|
||||
char version[9];
|
||||
int pos = 0;
|
||||
size_t len;
|
||||
char *space_prefix;
|
||||
|
||||
if ((out->tree_spec->type == CFG_DEF_TREE_DIFF) &&
|
||||
(!(out->tree_spec->check_status[cn->id] & CFG_DIFF)))
|
||||
return 1;
|
||||
|
||||
fprintf(out->fp, "%s%s\n", (out->tree_spec->type != CFG_DEF_TREE_CURRENT) &&
|
||||
(out->tree_spec->type != CFG_DEF_TREE_DIFF) &&
|
||||
(cfg_def->flags & CFG_DEFAULT_UNDEFINED) ? "#" : "", line);
|
||||
cfg_def = cfg_def_get_item_p(cn->id);
|
||||
|
||||
if (out->tree_spec->type == CFG_DEF_TREE_LIST) {
|
||||
/* List view with node paths and summary. */
|
||||
if (cfg_def->type & CFG_TYPE_SECTION)
|
||||
return 1;
|
||||
if (!_cfg_def_make_path(config_path, CFG_PATH_MAX_LEN, cfg_def->id, cfg_def, 1))
|
||||
return_0;
|
||||
if (out->tree_spec->withversions && !_get_config_node_version(cfg_def->since_version, version))
|
||||
return_0;
|
||||
|
||||
summary[0] = '\0';
|
||||
if (out->tree_spec->withsummary && cfg_def->comment)
|
||||
_copy_one_line(cfg_def->comment, summary, &pos, strlen(cfg_def->comment));
|
||||
|
||||
fprintf(out->fp, "%s%s%s%s%s%s%s\n", config_path,
|
||||
*summary || out->tree_spec->withversions ? " - ": "",
|
||||
*summary ? summary : "",
|
||||
*summary ? " " : "",
|
||||
out->tree_spec->withversions ? "[" : "",
|
||||
out->tree_spec->withversions ? version : "",
|
||||
out->tree_spec->withversions ? "]" : "");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Usual tree view with nodes and their values. */
|
||||
|
||||
if ((out->tree_spec->type != CFG_DEF_TREE_CURRENT) &&
|
||||
(out->tree_spec->type != CFG_DEF_TREE_DIFF) &&
|
||||
(out->tree_spec->type != CFG_DEF_TREE_FULL) &&
|
||||
(cfg_def->flags & (CFG_DEFAULT_UNDEFINED | CFG_DEFAULT_COMMENTED))) {
|
||||
/* print with # at the front to comment out the line */
|
||||
if (_should_print_cfg_with_undef_def_val(out, cfg_def, cn)) {
|
||||
space_prefix = ((len = strspn(line, "\t "))) ? dm_pool_strndup(out->mem, line, len) : NULL;
|
||||
fprintf(out->fp, "%s%s%s\n", space_prefix ? : "", "# ", line + len);
|
||||
if (space_prefix)
|
||||
dm_pool_free(out->mem, space_prefix);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* print the line as it is */
|
||||
if (_should_print_cfg_with_undef_def_val(out, cfg_def, cn))
|
||||
fprintf(out->fp, "%s\n", line);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1662,20 +1872,31 @@ static struct dm_config_node *_add_def_node(struct dm_config_tree *cft,
|
||||
{
|
||||
struct dm_config_node *cn;
|
||||
const char *str;
|
||||
uint32_t format_flags = 0;
|
||||
|
||||
if (!(cn = dm_config_create_node(cft, def->name))) {
|
||||
log_error("Failed to create default config setting node.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(def->type & CFG_TYPE_SECTION) && (!(cn->v = dm_config_create_value(cft)))) {
|
||||
log_error("Failed to create default config setting node value.");
|
||||
return NULL;
|
||||
if (!(def->type & CFG_TYPE_SECTION) && !(def->type & CFG_TYPE_ARRAY)) {
|
||||
if (!(cn->v = dm_config_create_value(cft))) {
|
||||
log_error("Failed to create default config setting node value.");
|
||||
return NULL;
|
||||
}
|
||||
if (spec->withspaces)
|
||||
format_flags |= DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES;
|
||||
}
|
||||
|
||||
cn->id = def->id;
|
||||
|
||||
if (!(def->type & CFG_TYPE_ARRAY)) {
|
||||
if (spec->unconfigured && def->default_unconfigured_value.v_UNCONFIGURED) {
|
||||
cn->v->type = DM_CFG_STRING;
|
||||
cn->v->v.str = cfg_def_get_default_unconfigured_value_hint(spec->cmd, def);
|
||||
if (def->type != CFG_TYPE_STRING)
|
||||
format_flags |= DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES;
|
||||
dm_config_value_set_format_flags(cn->v, format_flags);
|
||||
} else if (!(def->type & CFG_TYPE_ARRAY)) {
|
||||
switch (def->type) {
|
||||
case CFG_TYPE_SECTION:
|
||||
cn->v = NULL;
|
||||
@@ -1687,6 +1908,8 @@ static struct dm_config_node *_add_def_node(struct dm_config_tree *cft,
|
||||
case CFG_TYPE_INT:
|
||||
cn->v->type = DM_CFG_INT;
|
||||
cn->v->v.i = cfg_def_get_default_value_hint(spec->cmd, def, CFG_TYPE_INT, NULL);
|
||||
if (def->flags & CFG_FORMAT_INT_OCTAL)
|
||||
format_flags |= DM_CONFIG_VALUE_FMT_INT_OCTAL;
|
||||
break;
|
||||
case CFG_TYPE_FLOAT:
|
||||
cn->v->type = DM_CFG_FLOAT;
|
||||
@@ -1697,17 +1920,19 @@ static struct dm_config_node *_add_def_node(struct dm_config_tree *cft,
|
||||
if (!(str = cfg_def_get_default_value_hint(spec->cmd, def, CFG_TYPE_STRING, NULL)))
|
||||
str = "";
|
||||
cn->v->v.str = str;
|
||||
|
||||
if (spec->unconfigured && def->unconfigured_path)
|
||||
cn->v->v.str = def->unconfigured_path;
|
||||
break;
|
||||
default:
|
||||
log_error(INTERNAL_ERROR "_add_def_node: unknown type");
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
cn->v = _get_def_array_values(cft, def);
|
||||
dm_config_value_set_format_flags(cn->v, format_flags);
|
||||
} else {
|
||||
if (spec->withspaces)
|
||||
format_flags |= DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES;
|
||||
format_flags |= DM_CONFIG_VALUE_FMT_COMMON_ARRAY;
|
||||
cn->v = _get_def_array_values(spec->cmd, cft, def, format_flags);
|
||||
}
|
||||
|
||||
cn->child = NULL;
|
||||
if (parent) {
|
||||
@@ -1723,6 +1948,11 @@ static struct dm_config_node *_add_def_node(struct dm_config_tree *cft,
|
||||
return cn;
|
||||
}
|
||||
|
||||
static int _should_skip_deprecated_def_node(cfg_def_item_t *def, struct config_def_tree_spec *spec)
|
||||
{
|
||||
return spec->ignoredeprecated && _def_node_is_deprecated(def, spec);
|
||||
}
|
||||
|
||||
static int _should_skip_def_node(struct config_def_tree_spec *spec, int section_id, int id)
|
||||
{
|
||||
cfg_def_item_t *def = cfg_def_get_item_p(id);
|
||||
@@ -1734,6 +1964,8 @@ static int _should_skip_def_node(struct config_def_tree_spec *spec, int section_
|
||||
return 1;
|
||||
|
||||
switch (spec->type) {
|
||||
case CFG_DEF_TREE_FULL:
|
||||
/* fall through */
|
||||
case CFG_DEF_TREE_MISSING:
|
||||
if (!spec->check_status) {
|
||||
log_error_once(INTERNAL_ERROR "couldn't determine missing "
|
||||
@@ -1741,19 +1973,27 @@ static int _should_skip_def_node(struct config_def_tree_spec *spec, int section_
|
||||
return 1;
|
||||
}
|
||||
if ((spec->check_status[id] & CFG_USED) ||
|
||||
(def->flags & CFG_NAME_VARIABLE) ||
|
||||
(def->since_version > spec->version))
|
||||
(def->flags & CFG_NAME_VARIABLE))
|
||||
return 1;
|
||||
|
||||
if ((spec->type == CFG_DEF_TREE_MISSING) &&
|
||||
((def->since_version > spec->version) ||
|
||||
_should_skip_deprecated_def_node(def, spec)))
|
||||
return 1;
|
||||
break;
|
||||
case CFG_DEF_TREE_NEW:
|
||||
if (def->since_version != spec->version)
|
||||
if ((def->since_version != spec->version) ||
|
||||
_should_skip_deprecated_def_node(def, spec))
|
||||
return 1;
|
||||
break;
|
||||
case CFG_DEF_TREE_PROFILABLE:
|
||||
/* fall through */
|
||||
case CFG_DEF_TREE_PROFILABLE_CMD:
|
||||
/* fall through */
|
||||
case CFG_DEF_TREE_PROFILABLE_MDA:
|
||||
if (!(def->flags & CFG_PROFILABLE) ||
|
||||
(def->since_version > spec->version))
|
||||
(def->since_version > spec->version) ||
|
||||
_should_skip_deprecated_def_node(def, spec))
|
||||
return 1;
|
||||
flags = def->flags & ~CFG_PROFILABLE;
|
||||
if (spec->type == CFG_DEF_TREE_PROFILABLE_CMD) {
|
||||
@@ -1765,7 +2005,8 @@ static int _should_skip_def_node(struct config_def_tree_spec *spec, int section_
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (def->since_version > spec->version)
|
||||
if ((def->since_version > spec->version) ||
|
||||
_should_skip_deprecated_def_node(def, spec))
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
@@ -1804,7 +2045,7 @@ bad:
|
||||
|
||||
struct dm_config_tree *config_def_create_tree(struct config_def_tree_spec *spec)
|
||||
{
|
||||
struct dm_config_tree *cft;
|
||||
struct dm_config_tree *cft = NULL, *tmp_cft = NULL;
|
||||
struct dm_config_node *root = NULL, *relay = NULL, *tmp;
|
||||
int id;
|
||||
|
||||
@@ -1828,7 +2069,33 @@ struct dm_config_tree *config_def_create_tree(struct config_def_tree_spec *spec)
|
||||
}
|
||||
|
||||
cft->root = root;
|
||||
|
||||
if (spec->type == CFG_DEF_TREE_FULL) {
|
||||
if (!(tmp_cft = dm_config_create())) {
|
||||
log_error("Failed to create temporary config tree while creating full tree.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(tmp_cft->root = dm_config_clone_node_with_mem(cft->mem, spec->current_cft->root, 1))) {
|
||||
log_error("Failed to clone current config tree.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!merge_config_tree(spec->cmd, cft, tmp_cft, CONFIG_MERGE_TYPE_RAW)) {
|
||||
log_error("Failed to merge default and current config tree.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
dm_config_destroy(tmp_cft);
|
||||
}
|
||||
|
||||
return cft;
|
||||
bad:
|
||||
if (cft)
|
||||
dm_config_destroy(cft);
|
||||
if (tmp_cft)
|
||||
dm_config_destroy(tmp_cft);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _check_profile(struct cmd_context *cmd, struct profile *profile)
|
||||
@@ -2007,6 +2274,11 @@ const char *get_default_devices_cache_dir_CFG(struct cmd_context *cmd, struct pr
|
||||
return dm_pool_strdup(cmd->mem, buf);
|
||||
}
|
||||
|
||||
const char *get_default_unconfigured_devices_cache_dir_CFG(struct cmd_context *cmd)
|
||||
{
|
||||
return "@DEFAULT_SYS_DIR@/@DEFAULT_CACHE_SUBDIR@";
|
||||
}
|
||||
|
||||
const char *get_default_devices_cache_CFG(struct cmd_context *cmd, struct profile *profile)
|
||||
{
|
||||
const char *cache_dir = NULL, *cache_file_prefix = NULL;
|
||||
@@ -2041,6 +2313,24 @@ const char *get_default_devices_cache_CFG(struct cmd_context *cmd, struct profil
|
||||
return dm_pool_strdup(cmd->mem, buf);
|
||||
}
|
||||
|
||||
const char *get_default_unconfigured_devices_cache_CFG(struct cmd_context *cmd)
|
||||
{
|
||||
const char *cache_file_prefix = NULL;
|
||||
static char buf[PATH_MAX];
|
||||
|
||||
if (find_config_tree_node(cmd, devices_cache_file_prefix_CFG, NULL))
|
||||
cache_file_prefix = find_config_tree_str_allow_empty(cmd, devices_cache_file_prefix_CFG, NULL);
|
||||
|
||||
if (dm_snprintf(buf, sizeof(buf), "%s/%s.cache",
|
||||
get_default_unconfigured_devices_cache_dir_CFG(cmd),
|
||||
cache_file_prefix ? : DEFAULT_CACHE_FILE_PREFIX) < 0) {
|
||||
log_error("Persistent cache filename too long.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dm_pool_strdup(cmd->mem, buf);
|
||||
}
|
||||
|
||||
const char *get_default_backup_backup_dir_CFG(struct cmd_context *cmd, struct profile *profile)
|
||||
{
|
||||
static char buf[PATH_MAX];
|
||||
@@ -2054,6 +2344,11 @@ const char *get_default_backup_backup_dir_CFG(struct cmd_context *cmd, struct pr
|
||||
return dm_pool_strdup(cmd->mem, buf);
|
||||
}
|
||||
|
||||
const char *get_default_unconfigured_backup_backup_dir_CFG(struct cmd_context *cmd)
|
||||
{
|
||||
return "@DEFAULT_SYS_DIR@/@DEFAULT_BACKUP_SUBDIR@";
|
||||
}
|
||||
|
||||
const char *get_default_backup_archive_dir_CFG(struct cmd_context *cmd, struct profile *profile)
|
||||
{
|
||||
static char buf[PATH_MAX];
|
||||
@@ -2067,6 +2362,11 @@ const char *get_default_backup_archive_dir_CFG(struct cmd_context *cmd, struct p
|
||||
return dm_pool_strdup(cmd->mem, buf);
|
||||
}
|
||||
|
||||
const char *get_default_unconfigured_backup_archive_dir_CFG(struct cmd_context *cmd)
|
||||
{
|
||||
return "@DEFAULT_SYS_DIR@/@DEFAULT_ARCHIVE_SUBDIR@";
|
||||
}
|
||||
|
||||
const char *get_default_config_profile_dir_CFG(struct cmd_context *cmd, struct profile *profile)
|
||||
{
|
||||
static char buf[PATH_MAX];
|
||||
@@ -2080,6 +2380,11 @@ const char *get_default_config_profile_dir_CFG(struct cmd_context *cmd, struct p
|
||||
return dm_pool_strdup(cmd->mem, buf);
|
||||
}
|
||||
|
||||
const char *get_default_unconfigured_config_profile_dir_CFG(struct cmd_context *cmd)
|
||||
{
|
||||
return "@DEFAULT_SYS_DIR@/@DEFAULT_PROFILE_SUBDIR@";
|
||||
}
|
||||
|
||||
const char *get_default_activation_mirror_image_fault_policy_CFG(struct cmd_context *cmd, struct profile *profile)
|
||||
{
|
||||
return find_config_tree_str(cmd, activation_mirror_device_fault_policy_CFG, profile);
|
||||
@@ -2111,3 +2416,27 @@ int get_default_allocation_cache_pool_chunk_size_CFG(struct cmd_context *cmd, st
|
||||
{
|
||||
return DEFAULT_CACHE_POOL_CHUNK_SIZE * 2;
|
||||
}
|
||||
|
||||
const char *get_default_allocation_cache_policy_CFG(struct cmd_context *cmd, struct profile *profile)
|
||||
{
|
||||
const struct segment_type *segtype = get_segtype_from_string(cmd, "cache");
|
||||
unsigned attr = ~0;
|
||||
|
||||
if (!segtype ||
|
||||
!segtype->ops->target_present ||
|
||||
!segtype->ops->target_present(cmd, NULL, &attr)) {
|
||||
log_warn("WARNING: Cannot detect default cache policy, using \""
|
||||
DEFAULT_CACHE_POLICY "\".");
|
||||
return DEFAULT_CACHE_POLICY;
|
||||
}
|
||||
|
||||
if (attr & CACHE_FEATURE_POLICY_SMQ)
|
||||
return "smq";
|
||||
|
||||
if (attr & CACHE_FEATURE_POLICY_MQ)
|
||||
return "mq";
|
||||
|
||||
log_warn("WARNING: Default cache policy not available.");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ struct profile_params {
|
||||
struct dm_list profiles; /* list of profiles which are loaded already and which are ready for use */
|
||||
};
|
||||
|
||||
#define CFG_PATH_MAX_LEN 64
|
||||
#define CFG_PATH_MAX_LEN 128
|
||||
|
||||
/*
|
||||
* Structures used for definition of a configuration tree.
|
||||
@@ -72,6 +72,7 @@ typedef int (*t_fn_CFG_TYPE_INT) (struct cmd_context *cmd, struct profile *profi
|
||||
typedef float (*t_fn_CFG_TYPE_FLOAT) (struct cmd_context *cmd, struct profile *profile);
|
||||
typedef const char* (*t_fn_CFG_TYPE_STRING) (struct cmd_context *cmd, struct profile *profile);
|
||||
typedef const char* (*t_fn_CFG_TYPE_ARRAY) (struct cmd_context *cmd, struct profile *profile);
|
||||
typedef const char* (*t_fn_UNCONFIGURED) (struct cmd_context *cmd);
|
||||
|
||||
/* configuration definition item value (for item's default value) */
|
||||
typedef union {
|
||||
@@ -88,6 +89,11 @@ typedef union {
|
||||
t_fn_CFG_TYPE_ARRAY fn_CFG_TYPE_ARRAY;
|
||||
} cfg_def_value_t;
|
||||
|
||||
typedef union {
|
||||
const char *v_UNCONFIGURED;
|
||||
t_fn_UNCONFIGURED fn_UNCONFIGURED;
|
||||
} cfg_def_unconfigured_value_t;
|
||||
|
||||
/* configuration definition item flags: */
|
||||
|
||||
|
||||
@@ -107,50 +113,62 @@ typedef union {
|
||||
#define CFG_PROFILABLE_METADATA 0x030
|
||||
/* whether the default value is undefned */
|
||||
#define CFG_DEFAULT_UNDEFINED 0x040
|
||||
/* whether the default value is commented out on output */
|
||||
#define CFG_DEFAULT_COMMENTED 0x080
|
||||
/* whether the default value is calculated during run time */
|
||||
#define CFG_DEFAULT_RUN_TIME 0x080
|
||||
#define CFG_DEFAULT_RUN_TIME 0x100
|
||||
/* whether the configuration setting is disabled (and hence defaults always used) */
|
||||
#define CFG_DISABLED 0x100
|
||||
#define CFG_DISABLED 0x200
|
||||
/* whether to print integers in octal form (prefixed by "0") */
|
||||
#define CFG_FORMAT_INT_OCTAL 0x400
|
||||
/* whether to disable checks for the whole config section subtree */
|
||||
#define CFG_SECTION_NO_CHECK 0x800
|
||||
|
||||
/* configuration definition item structure */
|
||||
typedef struct cfg_def_item {
|
||||
int id; /* ID of this item */
|
||||
int parent; /* ID of parent item */
|
||||
const char *name; /* name of the item in configuration tree */
|
||||
int type; /* configuration item type (bits of cfg_def_type_t) */
|
||||
cfg_def_value_t default_value; /* default value (only for settings) */
|
||||
uint16_t flags; /* configuration item definition flags */
|
||||
uint16_t since_version; /* version this item appeared in */
|
||||
const char *unconfigured_path; /* path in terms of @FOO@, pre-configured */
|
||||
const char *comment; /* brief comment */
|
||||
int id; /* ID of this item */
|
||||
int parent; /* ID of parent item */
|
||||
const char *name; /* name of the item in configuration tree */
|
||||
int type; /* configuration item type (bits of cfg_def_type_t) */
|
||||
cfg_def_value_t default_value; /* default value (only for settings) */
|
||||
uint16_t flags; /* configuration item definition flags */
|
||||
uint16_t since_version; /* version this item appeared in */
|
||||
cfg_def_unconfigured_value_t default_unconfigured_value; /* default value in terms of @FOO@, pre-configured (only for settings) */
|
||||
uint16_t deprecated_since_version; /* version since this item is deprecated */
|
||||
const char *deprecation_comment; /* comment about reasons for deprecation and settings that supersede this one */
|
||||
const char *comment; /* comment */
|
||||
} cfg_def_item_t;
|
||||
|
||||
/* configuration definition tree types */
|
||||
typedef enum {
|
||||
CFG_DEF_TREE_CURRENT, /* tree of nodes with values currently set in the config */
|
||||
CFG_DEF_TREE_MISSING, /* tree of nodes missing in current config using default values */
|
||||
CFG_DEF_TREE_COMPLETE, /* CURRENT + MISSING, the tree actually used within execution, not implemented yet */
|
||||
CFG_DEF_TREE_FULL, /* CURRENT + MISSING, the tree actually used within execution */
|
||||
CFG_DEF_TREE_DEFAULT, /* tree of all possible config nodes with default values */
|
||||
CFG_DEF_TREE_NEW, /* tree of all new nodes that appeared in given version */
|
||||
CFG_DEF_TREE_PROFILABLE, /* tree of all nodes that are customizable by profiles */
|
||||
CFG_DEF_TREE_PROFILABLE_CMD, /* tree of all nodes that are customizable by command profiles (subset of PROFILABLE) */
|
||||
CFG_DEF_TREE_PROFILABLE_MDA, /* tree of all nodes that are customizable by metadata profiles (subset of PROFILABLE) */
|
||||
CFG_DEF_TREE_DIFF, /* tree of all nodes that differ from defaults */
|
||||
CFG_DEF_TREE_LIST, /* list all nodes */
|
||||
} cfg_def_tree_t;
|
||||
|
||||
/* configuration definition tree specification */
|
||||
struct config_def_tree_spec {
|
||||
struct cmd_context *cmd; /* command context (for run-time defaults */
|
||||
cfg_def_tree_t type; /* tree type */
|
||||
uint16_t version; /* tree at this LVM2 version */
|
||||
struct cmd_context *cmd; /* command context (for run-time defaults */
|
||||
struct dm_config_tree *current_cft; /* current config tree which is defined explicitly - defaults are not used */
|
||||
cfg_def_tree_t type; /* tree type */
|
||||
uint16_t version; /* tree at this LVM2 version */
|
||||
unsigned ignoreadvanced:1; /* do not include advanced configs */
|
||||
unsigned ignoreunsupported:1; /* do not include unsupported configs */
|
||||
unsigned ignorelocal:1; /* do not include the local section */
|
||||
unsigned withcomments:1; /* include first line of comment */
|
||||
unsigned withfullcomments:1; /* include all comment lines */
|
||||
unsigned ignoreunsupported:1; /* do not include unsupported configs */
|
||||
unsigned ignoredeprecated:1; /* do not include deprecated configs */
|
||||
unsigned ignorelocal:1; /* do not include the local section */
|
||||
unsigned withsummary:1; /* include first line of comments - a summary */
|
||||
unsigned withcomments:1; /* include all comment lines */
|
||||
unsigned withversions:1; /* include versions */
|
||||
unsigned withspaces:1; /* add more spaces in output for better readability */
|
||||
unsigned unconfigured:1; /* use unconfigured path strings */
|
||||
uint8_t *check_status; /* status of last tree check (currently needed for CFG_DEF_TREE_MISSING only) */
|
||||
uint8_t *check_status; /* status of last tree check (currently needed for CFG_DEF_TREE_MISSING only) */
|
||||
};
|
||||
|
||||
|
||||
@@ -165,11 +183,11 @@ struct config_def_tree_spec {
|
||||
* Register ID for each possible item in the configuration tree.
|
||||
*/
|
||||
enum {
|
||||
#define cfg_section(id, name, parent, flags, since_version, unconfigured_path, comment) id,
|
||||
#define cfg(id, name, parent, flags, type, default_value, since_version, unconfigured_path, comment) id,
|
||||
#define cfg_runtime(id, name, parent, flags, type, since_version, unconfigured_path, comment) id,
|
||||
#define cfg_array(id, name, parent, flags, types, default_value, since_version, unconfigured_path, comment) id,
|
||||
#define cfg_array_runtime(id, name, parent, flags, types, since_version, unconfigured_path, comment) id,
|
||||
#define cfg_section(id, name, parent, flags, since_version, deprecated_since_version, deprecation_comment, comment) id,
|
||||
#define cfg(id, name, parent, flags, type, default_value, since_version, unconfigured_value, deprecated_since_version, deprecation_comment, comment) id,
|
||||
#define cfg_runtime(id, name, parent, flags, type, since_version, deprecated_since_version, deprecation_comment, comment) id,
|
||||
#define cfg_array(id, name, parent, flags, types, default_value, since_version, unconfigured_value, deprecated_since_version, deprecation_comment, comment) id,
|
||||
#define cfg_array_runtime(id, name, parent, flags, types, since_version, deprecated_since_version, deprecation_comment, comment) id,
|
||||
#include "config_settings.h"
|
||||
#undef cfg_section
|
||||
#undef cfg
|
||||
@@ -191,6 +209,8 @@ struct cft_check_handle {
|
||||
unsigned skip_if_checked:1; /* skip the check if already done before - return last state */
|
||||
unsigned suppress_messages:1; /* suppress messages during the check if config item is found invalid */
|
||||
unsigned check_diff:1; /* check if the value used differs from default one */
|
||||
unsigned ignoreadvanced:1; /* do not include advnced configs */
|
||||
unsigned ignoreunsupported:1; /* do not include unsupported configs */
|
||||
uint8_t status[CFG_COUNT]; /* flags for each configuration item - the result of the check */
|
||||
};
|
||||
|
||||
@@ -254,18 +274,29 @@ int find_config_tree_int(struct cmd_context *cmd, int id, struct profile *profil
|
||||
int64_t find_config_tree_int64(struct cmd_context *cmd, int id, struct profile *profile);
|
||||
float find_config_tree_float(struct cmd_context *cmd, int id, struct profile *profile);
|
||||
int find_config_tree_bool(struct cmd_context *cmd, int id, struct profile *profile);
|
||||
const struct dm_config_node *find_config_tree_array(struct cmd_context *cmd, int id, struct profile *profile);
|
||||
|
||||
/*
|
||||
* Functions for configuration settings for which the default
|
||||
* value is evaluated at runtime based on command context.
|
||||
*/
|
||||
const char *get_default_devices_cache_dir_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
const char *get_default_unconfigured_devices_cache_dir_CFG(struct cmd_context *cmd);
|
||||
const char *get_default_devices_cache_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
const char *get_default_unconfigured_devices_cache_CFG(struct cmd_context *cmd);
|
||||
const char *get_default_backup_backup_dir_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
const char *get_default_unconfigured_backup_backup_dir_CFG(struct cmd_context *cmd);
|
||||
const char *get_default_backup_archive_dir_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
const char *get_default_unconfigured_backup_archive_dir_CFG(struct cmd_context *cmd);
|
||||
const char *get_default_config_profile_dir_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
const char *get_default_unconfigured_config_profile_dir_CFG(struct cmd_context *cmd);
|
||||
const char *get_default_activation_mirror_image_fault_policy_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
#define get_default_unconfigured_activation_mirror_image_fault_policy_CFG NULL
|
||||
int get_default_allocation_thin_pool_chunk_size_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
#define get_default_unconfigured_allocation_thin_pool_chunk_size_CFG NULL
|
||||
int get_default_allocation_cache_pool_chunk_size_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
#define get_default_unconfigured_allocation_cache_pool_chunk_size_CFG NULL
|
||||
const char *get_default_allocation_cache_policy_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
#define get_default_unconfigured_allocation_cache_policy_CFG NULL
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -51,11 +51,14 @@
|
||||
#define DEFAULT_FALLBACK_TO_LOCAL_LOCKING 1
|
||||
#define DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING 1
|
||||
#define DEFAULT_WAIT_FOR_LOCKS 1
|
||||
#define DEFAULT_LVMLOCKD_LOCK_RETRIES 3
|
||||
#define DEFAULT_PRIORITISE_WRITE_LOCKS 1
|
||||
#define DEFAULT_USE_MLOCKALL 0
|
||||
#define DEFAULT_METADATA_READ_ONLY 0
|
||||
#define DEFAULT_LVDISPLAY_SHOWS_FULL_DEVICE_PATH 0
|
||||
|
||||
#define DEFAULT_SANLOCK_LV_EXTEND_MB 256
|
||||
|
||||
#define DEFAULT_MIRRORLOG MIRROR_LOG_DISK
|
||||
#define DEFAULT_MIRROR_LOG_FAULT_POLICY "allocate"
|
||||
#define DEFAULT_MIRROR_IMAGE_FAULT_POLICY "remove"
|
||||
@@ -78,12 +81,15 @@
|
||||
#ifdef THIN_CHECK_NEEDS_CHECK
|
||||
# define DEFAULT_THIN_CHECK_OPTION1 "-q"
|
||||
# define DEFAULT_THIN_CHECK_OPTION2 "--clear-needs-check-flag"
|
||||
# define DEFAULT_THIN_CHECK_OPTIONS_CONFIG "#S" DEFAULT_THIN_CHECK_OPTION1 "#S" DEFAULT_THIN_CHECK_OPTION2
|
||||
#else
|
||||
# define DEFAULT_THIN_CHECK_OPTION1 "-q"
|
||||
# define DEFAULT_THIN_CHECK_OPTION2 ""
|
||||
# define DEFAULT_THIN_CHECK_OPTIONS_CONFIG "#S" DEFAULT_THIN_CHECK_OPTION1
|
||||
#endif
|
||||
|
||||
#define DEFAULT_THIN_REPAIR_OPTIONS ""
|
||||
#define DEFAULT_THIN_REPAIR_OPTION1 ""
|
||||
#define DEFAULT_THIN_REPAIR_OPTIONS_CONFIG "#S" DEFAULT_THIN_REPAIR_OPTION1
|
||||
#define DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS 0
|
||||
#define DEFAULT_THIN_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024) /* KB */
|
||||
#define DEFAULT_THIN_POOL_MIN_METADATA_SIZE 2048 /* KB */
|
||||
@@ -95,23 +101,27 @@
|
||||
#define DEFAULT_THIN_POOL_ZERO 1
|
||||
#define DEFAULT_POOL_METADATA_SPARE 1 /* thin + cache */
|
||||
|
||||
#define DEFAULT_CACHE_CHECK_OPTION1 "-q"
|
||||
#define DEFAULT_CACHE_REPAIR_OPTIONS ""
|
||||
#ifdef CACHE_CHECK_NEEDS_CHECK
|
||||
# define DEFAULT_CACHE_CHECK_OPTION1 "-q"
|
||||
# define DEFAULT_CACHE_CHECK_OPTION2 "--clear-needs-check-flag"
|
||||
# define DEFAULT_CACHE_CHECK_OPTIONS_CONFIG "#S" DEFAULT_CACHE_CHECK_OPTION1 "#S" DEFAULT_CACHE_CHECK_OPTION2
|
||||
#else
|
||||
# define DEFAULT_CACHE_CHECK_OPTION1 "-q"
|
||||
# define DEFAULT_CACHE_CHECK_OPTION2 ""
|
||||
# define DEFAULT_CACHE_CHECK_OPTIONS_CONFIG "#S" DEFAULT_CACHE_CHECK_OPTION1
|
||||
#endif
|
||||
|
||||
#define DEFAULT_CACHE_REPAIR_OPTION1 ""
|
||||
#define DEFAULT_CACHE_REPAIR_OPTIONS_CONFIG "#S" DEFAULT_CACHE_REPAIR_OPTION1
|
||||
#define DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS 0
|
||||
#define DEFAULT_CACHE_POOL_CHUNK_SIZE 64 /* KB */
|
||||
#define DEFAULT_CACHE_POOL_MIN_METADATA_SIZE 2048 /* KB */
|
||||
#define DEFAULT_CACHE_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024) /* KB */
|
||||
#define DEFAULT_CACHE_POOL_CACHEMODE "writethrough"
|
||||
#define DEFAULT_CACHE_POOL_POLICY "mq"
|
||||
#define DEFAULT_CACHE_POLICY "mq"
|
||||
#define DEFAULT_CACHE_MODE "writethrough"
|
||||
|
||||
#define DEFAULT_UMASK 0077
|
||||
|
||||
#ifdef LVM1_FALLBACK
|
||||
# define DEFAULT_FALLBACK_TO_LVM1 1
|
||||
#else
|
||||
# define DEFAULT_FALLBACK_TO_LVM1 0
|
||||
#endif
|
||||
|
||||
#define DEFAULT_FORMAT "lvm2"
|
||||
|
||||
#define DEFAULT_STRIPESIZE 64 /* KB */
|
||||
@@ -142,10 +152,6 @@
|
||||
# define DEFAULT_LOG_FACILITY LOG_USER
|
||||
#endif
|
||||
|
||||
#define DEFAULT_LOGGED_DEBUG_CLASSES (LOG_CLASS_MEM | LOG_CLASS_DEVS | \
|
||||
LOG_CLASS_ACTIVATION | LOG_CLASS_ALLOC | LOG_CLASS_LVMETAD | \
|
||||
LOG_CLASS_METADATA | LOG_CLASS_CACHE | LOG_CLASS_LOCKING)
|
||||
|
||||
#define DEFAULT_SYSLOG 1
|
||||
#define DEFAULT_VERBOSE 0
|
||||
#define DEFAULT_SILENT 0
|
||||
@@ -191,6 +197,7 @@
|
||||
#define DEFAULT_REP_QUOTED 1
|
||||
#define DEFAULT_REP_SEPARATOR " "
|
||||
#define DEFAULT_REP_LIST_ITEM_SEPARATOR ","
|
||||
#define DEFAULT_TIME_FORMAT "%Y-%m-%d %T %z"
|
||||
|
||||
#define DEFAULT_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"
|
||||
#define DEFAULT_VGS_COLS "vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free"
|
||||
@@ -220,4 +227,6 @@
|
||||
#define DEFAULT_THIN_POOL_AUTOEXTEND_THRESHOLD 100
|
||||
#define DEFAULT_THIN_POOL_AUTOEXTEND_PERCENT 20
|
||||
|
||||
#define DEFAULT_CY_LOCK_TYPE "sanlock"
|
||||
|
||||
#endif /* _LVM_DEFAULTS_H */
|
||||
|
||||
@@ -681,10 +681,12 @@ static int _init_preferred_names(struct cmd_context *cmd)
|
||||
|
||||
_cache.preferred_names_matcher = NULL;
|
||||
|
||||
if (!(cn = find_config_tree_node(cmd, devices_preferred_names_CFG, NULL)) ||
|
||||
if (!(cn = find_config_tree_array(cmd, devices_preferred_names_CFG, NULL)) ||
|
||||
cn->v->type == DM_CFG_EMPTY_ARRAY) {
|
||||
log_very_verbose("devices/preferred_names not found in config file: "
|
||||
"using built-in preferences");
|
||||
log_very_verbose("devices/preferred_names %s: "
|
||||
"using built-in preferences",
|
||||
cn && cn->v->type == DM_CFG_EMPTY_ARRAY ? "is empty"
|
||||
: "not found in config");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -943,7 +945,7 @@ struct device *dev_cache_get(const char *name, struct dev_filter *f)
|
||||
if (d)
|
||||
dm_hash_remove(_cache.names, name);
|
||||
log_sys_very_verbose("stat", name);
|
||||
return NULL;
|
||||
d = NULL;
|
||||
}
|
||||
|
||||
if (d && (buf.st_rdev != d->dev)) {
|
||||
|
||||
@@ -225,6 +225,9 @@ int dev_subsystem_part_major(struct dev_types *dt, struct device *dev)
|
||||
|
||||
const char *dev_subsystem_name(struct dev_types *dt, struct device *dev)
|
||||
{
|
||||
if (MAJOR(dev->dev) == dt->device_mapper_major)
|
||||
return "DM";
|
||||
|
||||
if (MAJOR(dev->dev) == dt->md_major)
|
||||
return "MD";
|
||||
|
||||
@@ -430,7 +433,7 @@ int dev_get_primary_dev(struct dev_types *dt, struct device *dev, dev_t *result)
|
||||
*/
|
||||
if ((parts = dt->dev_type_array[major].max_partitions) > 1) {
|
||||
if ((residue = minor % parts)) {
|
||||
*result = MKDEV((dev_t)major, (minor - residue));
|
||||
*result = MKDEV((dev_t)major, (dev_t)(minor - residue));
|
||||
ret = 2;
|
||||
} else {
|
||||
*result = dev->dev;
|
||||
@@ -507,7 +510,7 @@ int dev_get_primary_dev(struct dev_types *dt, struct device *dev, dev_t *result)
|
||||
path, buffer);
|
||||
goto out;
|
||||
}
|
||||
*result = MKDEV((dev_t)major, minor);
|
||||
*result = MKDEV((dev_t)major, (dev_t)minor);
|
||||
ret = 2;
|
||||
out:
|
||||
if (fp && fclose(fp))
|
||||
@@ -525,12 +528,14 @@ static inline int _type_in_flag_list(const char *type, uint32_t flag_list)
|
||||
((flag_list & TYPE_DM_SNAPSHOT_COW) && !strcmp(type, "DM_snapshot_cow")));
|
||||
}
|
||||
|
||||
#define MSG_FAILED_SIG_OFFSET "Failed to get offset of the %s signature on %s."
|
||||
#define MSG_FAILED_SIG_LENGTH "Failed to get length of the %s signature on %s."
|
||||
#define MSG_WIPING_SKIPPED " Wiping skipped."
|
||||
|
||||
static int _blkid_wipe(blkid_probe probe, struct device *dev, const char *name,
|
||||
uint32_t types_to_exclude, uint32_t types_no_prompt,
|
||||
int yes, force_t force)
|
||||
{
|
||||
static const char _msg_failed_offset[] = "Failed to get offset of the %s signature on %s.";
|
||||
static const char _msg_failed_length[] = "Failed to get length of the %s signature on %s.";
|
||||
static const char _msg_wiping[] = "Wiping %s signature on %s.";
|
||||
const char *offset = NULL, *type = NULL, *magic = NULL,
|
||||
*usage = NULL, *label = NULL, *uuid = NULL;
|
||||
@@ -541,21 +546,41 @@ static int _blkid_wipe(blkid_probe probe, struct device *dev, const char *name,
|
||||
if (_type_in_flag_list(type, types_to_exclude))
|
||||
return 2;
|
||||
if (blkid_probe_lookup_value(probe, "SBMAGIC_OFFSET", &offset, NULL)) {
|
||||
log_error(_msg_failed_offset, type, name);
|
||||
return 0;
|
||||
if (force < DONT_PROMPT) {
|
||||
log_error(MSG_FAILED_SIG_OFFSET, type, name);
|
||||
return 0;
|
||||
} else {
|
||||
log_error("WARNING: " MSG_FAILED_SIG_OFFSET MSG_WIPING_SKIPPED, type, name);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
if (blkid_probe_lookup_value(probe, "SBMAGIC", &magic, &len)) {
|
||||
log_error(_msg_failed_length, type, name);
|
||||
return 0;
|
||||
if (force < DONT_PROMPT) {
|
||||
log_error(MSG_FAILED_SIG_LENGTH, type, name);
|
||||
return 0;
|
||||
} else {
|
||||
log_warn("WARNING: " MSG_FAILED_SIG_LENGTH MSG_WIPING_SKIPPED, type, name);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
} else if (!blkid_probe_lookup_value(probe, "PTTYPE", &type, NULL)) {
|
||||
if (blkid_probe_lookup_value(probe, "PTMAGIC_OFFSET", &offset, NULL)) {
|
||||
log_error(_msg_failed_offset, type, name);
|
||||
return 0;
|
||||
if (force < DONT_PROMPT) {
|
||||
log_error(MSG_FAILED_SIG_OFFSET, type, name);
|
||||
return 0;
|
||||
} else {
|
||||
log_warn("WARNING: " MSG_FAILED_SIG_OFFSET MSG_WIPING_SKIPPED, type, name);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
if (blkid_probe_lookup_value(probe, "PTMAGIC", &magic, &len)) {
|
||||
log_error(_msg_failed_length, type, name);
|
||||
return 0;
|
||||
if (force < DONT_PROMPT) {
|
||||
log_error(MSG_FAILED_SIG_LENGTH, type, name);
|
||||
return 0;
|
||||
} else {
|
||||
log_warn("WARNING: " MSG_FAILED_SIG_LENGTH MSG_WIPING_SKIPPED, type, name);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
usage = "partition table";
|
||||
} else
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#define SIZE_BUF 128
|
||||
|
||||
typedef enum { SIZE_LONG = 0, SIZE_SHORT = 1, SIZE_UNIT = 2 } size_len_t;
|
||||
|
||||
static const struct {
|
||||
alloc_policy_t alloc;
|
||||
const char str[14]; /* must be changed when size extends 13 chars */
|
||||
@@ -86,6 +82,38 @@ alloc_policy_t get_alloc_from_string(const char *str)
|
||||
return ALLOC_INVALID;
|
||||
}
|
||||
|
||||
const char *get_lock_type_string(lock_type_t lock_type)
|
||||
{
|
||||
switch (lock_type) {
|
||||
case LOCK_TYPE_INVALID:
|
||||
return "invalid";
|
||||
case LOCK_TYPE_NONE:
|
||||
return "none";
|
||||
case LOCK_TYPE_CLVM:
|
||||
return "clvm";
|
||||
case LOCK_TYPE_DLM:
|
||||
return "dlm";
|
||||
case LOCK_TYPE_SANLOCK:
|
||||
return "sanlock";
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
lock_type_t get_lock_type_from_string(const char *str)
|
||||
{
|
||||
if (!str)
|
||||
return LOCK_TYPE_NONE;
|
||||
if (!strcmp(str, "none"))
|
||||
return LOCK_TYPE_NONE;
|
||||
if (!strcmp(str, "clvm"))
|
||||
return LOCK_TYPE_CLVM;
|
||||
if (!strcmp(str, "dlm"))
|
||||
return LOCK_TYPE_DLM;
|
||||
if (!strcmp(str, "sanlock"))
|
||||
return LOCK_TYPE_SANLOCK;
|
||||
return LOCK_TYPE_INVALID;
|
||||
}
|
||||
|
||||
static const char *_percent_types[7] = { "NONE", "VG", "FREE", "LV", "PVS", "ORIGIN" };
|
||||
|
||||
const char *get_percent_string(percent_type_t def)
|
||||
@@ -95,168 +123,49 @@ const char *get_percent_string(percent_type_t def)
|
||||
|
||||
const char *display_lvname(const struct logical_volume *lv)
|
||||
{
|
||||
/* On allocation failure, just return the LV name. */
|
||||
return lv_fullname_dup(lv->vg->cmd->mem, lv) ? : lv->name;
|
||||
}
|
||||
char *name;
|
||||
int r;
|
||||
|
||||
#define BASE_UNKNOWN 0
|
||||
#define BASE_SHARED 1
|
||||
#define BASE_1024 8
|
||||
#define BASE_1000 15
|
||||
#define BASE_SPECIAL 21
|
||||
#define NUM_UNIT_PREFIXES 6
|
||||
#define NUM_SPECIAL 3
|
||||
if ((lv->vg->cmd->display_lvname_idx + NAME_LEN) >= sizeof((lv->vg->cmd->display_buffer)))
|
||||
lv->vg->cmd->display_lvname_idx = 0;
|
||||
|
||||
name = lv->vg->cmd->display_buffer + lv->vg->cmd->display_lvname_idx;
|
||||
r = dm_snprintf(name, NAME_LEN, "%s/%s", lv->vg->name, lv->name);
|
||||
|
||||
if (r < 0) {
|
||||
log_error("Full LV name \"%s/%s\" is too long.", lv->vg->name, lv->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv->vg->cmd->display_lvname_idx += r + 1;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Size supplied in sectors */
|
||||
static const char *_display_size(const struct cmd_context *cmd,
|
||||
uint64_t size, size_len_t sl)
|
||||
uint64_t size, dm_size_suffix_t suffix_type)
|
||||
{
|
||||
unsigned base = BASE_UNKNOWN;
|
||||
unsigned s;
|
||||
int suffix, precision;
|
||||
uint64_t byte = UINT64_C(0);
|
||||
uint64_t units = UINT64_C(1024);
|
||||
char *size_buf = NULL;
|
||||
const char * const size_str[][3] = {
|
||||
/* BASE_UNKNOWN */
|
||||
{" ", " ", " "}, /* [0] */
|
||||
|
||||
/* BASE_SHARED - Used if cmd->si_unit_consistency = 0 */
|
||||
{" Exabyte", " EB", "E"}, /* [1] */
|
||||
{" Petabyte", " PB", "P"}, /* [2] */
|
||||
{" Terabyte", " TB", "T"}, /* [3] */
|
||||
{" Gigabyte", " GB", "G"}, /* [4] */
|
||||
{" Megabyte", " MB", "M"}, /* [5] */
|
||||
{" Kilobyte", " KB", "K"}, /* [6] */
|
||||
{" Byte ", " B", "B"}, /* [7] */
|
||||
|
||||
/* BASE_1024 - Used if cmd->si_unit_consistency = 1 */
|
||||
{" Exbibyte", " EiB", "e"}, /* [8] */
|
||||
{" Pebibyte", " PiB", "p"}, /* [9] */
|
||||
{" Tebibyte", " TiB", "t"}, /* [10] */
|
||||
{" Gibibyte", " GiB", "g"}, /* [11] */
|
||||
{" Mebibyte", " MiB", "m"}, /* [12] */
|
||||
{" Kibibyte", " KiB", "k"}, /* [13] */
|
||||
{" Byte ", " B", "b"}, /* [14] */
|
||||
|
||||
/* BASE_1000 - Used if cmd->si_unit_consistency = 1 */
|
||||
{" Exabyte", " EB", "E"}, /* [15] */
|
||||
{" Petabyte", " PB", "P"}, /* [16] */
|
||||
{" Terabyte", " TB", "T"}, /* [17] */
|
||||
{" Gigabyte", " GB", "G"}, /* [18] */
|
||||
{" Megabyte", " MB", "M"}, /* [19] */
|
||||
{" Kilobyte", " kB", "K"}, /* [20] */
|
||||
|
||||
/* BASE_SPECIAL */
|
||||
{" Byte ", " B ", "B"}, /* [21] (shared with BASE_1000) */
|
||||
{" Units ", " Un", "U"}, /* [22] */
|
||||
{" Sectors ", " Se", "S"}, /* [23] */
|
||||
};
|
||||
|
||||
if (!(size_buf = dm_pool_alloc(cmd->mem, SIZE_BUF))) {
|
||||
log_error("no memory for size display buffer");
|
||||
return "";
|
||||
}
|
||||
|
||||
suffix = cmd->current_settings.suffix;
|
||||
|
||||
if (!cmd->si_unit_consistency) {
|
||||
/* Case-independent match */
|
||||
for (s = 0; s < NUM_UNIT_PREFIXES; s++)
|
||||
if (toupper((int) cmd->current_settings.unit_type) ==
|
||||
*size_str[BASE_SHARED + s][2]) {
|
||||
base = BASE_SHARED;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Case-dependent match for powers of 1000 */
|
||||
for (s = 0; s < NUM_UNIT_PREFIXES; s++)
|
||||
if (cmd->current_settings.unit_type ==
|
||||
*size_str[BASE_1000 + s][2]) {
|
||||
base = BASE_1000;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Case-dependent match for powers of 1024 */
|
||||
if (base == BASE_UNKNOWN)
|
||||
for (s = 0; s < NUM_UNIT_PREFIXES; s++)
|
||||
if (cmd->current_settings.unit_type ==
|
||||
*size_str[BASE_1024 + s][2]) {
|
||||
base = BASE_1024;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (base == BASE_UNKNOWN)
|
||||
/* Check for special units - s, b or u */
|
||||
for (s = 0; s < NUM_SPECIAL; s++)
|
||||
if (toupper((int) cmd->current_settings.unit_type) ==
|
||||
*size_str[BASE_SPECIAL + s][2]) {
|
||||
base = BASE_SPECIAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (size == UINT64_C(0)) {
|
||||
if (base == BASE_UNKNOWN)
|
||||
s = 0;
|
||||
sprintf(size_buf, "0%s", suffix ? size_str[base + s][sl] : "");
|
||||
return size_buf;
|
||||
}
|
||||
|
||||
size *= UINT64_C(512);
|
||||
|
||||
if (base != BASE_UNKNOWN)
|
||||
byte = cmd->current_settings.unit_factor;
|
||||
else {
|
||||
/* Human-readable style */
|
||||
if (cmd->current_settings.unit_type == 'H') {
|
||||
units = UINT64_C(1000);
|
||||
base = BASE_1000;
|
||||
} else {
|
||||
units = UINT64_C(1024);
|
||||
base = BASE_1024;
|
||||
}
|
||||
|
||||
if (!cmd->si_unit_consistency)
|
||||
base = BASE_SHARED;
|
||||
|
||||
byte = units * units * units * units * units * units;
|
||||
|
||||
for (s = 0; s < NUM_UNIT_PREFIXES && size < byte; s++)
|
||||
byte /= units;
|
||||
|
||||
suffix = 1;
|
||||
}
|
||||
|
||||
/* FIXME Make precision configurable */
|
||||
switch (toupper(*size_str[base + s][SIZE_UNIT])) {
|
||||
case 'B':
|
||||
case 'S':
|
||||
precision = 0;
|
||||
break;
|
||||
default:
|
||||
precision = 2;
|
||||
}
|
||||
|
||||
snprintf(size_buf, SIZE_BUF - 1, "%.*f%s", precision,
|
||||
(double) size / byte, suffix ? size_str[base + s][sl] : "");
|
||||
|
||||
return size_buf;
|
||||
return dm_size_to_string(cmd->mem, size, cmd->current_settings.unit_type,
|
||||
cmd->si_unit_consistency,
|
||||
cmd->current_settings.unit_factor,
|
||||
cmd->current_settings.suffix,
|
||||
suffix_type);
|
||||
}
|
||||
|
||||
const char *display_size_long(const struct cmd_context *cmd, uint64_t size)
|
||||
{
|
||||
return _display_size(cmd, size, SIZE_LONG);
|
||||
return _display_size(cmd, size, DM_SIZE_LONG);
|
||||
}
|
||||
|
||||
const char *display_size_units(const struct cmd_context *cmd, uint64_t size)
|
||||
{
|
||||
return _display_size(cmd, size, SIZE_UNIT);
|
||||
return _display_size(cmd, size, DM_SIZE_UNIT);
|
||||
}
|
||||
|
||||
const char *display_size(const struct cmd_context *cmd, uint64_t size)
|
||||
{
|
||||
return _display_size(cmd, size, SIZE_SHORT);
|
||||
return _display_size(cmd, size, DM_SIZE_SHORT);
|
||||
}
|
||||
|
||||
void pvdisplay_colons(const struct physical_volume *pv)
|
||||
@@ -474,7 +383,7 @@ int lvdisplay_full(struct cmd_context *cmd,
|
||||
log_print("LV UUID %s", uuid);
|
||||
log_print("LV Write Access %s", access_str);
|
||||
log_print("LV Creation host, time %s, %s",
|
||||
lv_host_dup(cmd->mem, lv), lv_time_dup(cmd->mem, lv));
|
||||
lv_host_dup(cmd->mem, lv), lv_time_dup(cmd->mem, lv, 1));
|
||||
|
||||
if (lv_is_origin(lv)) {
|
||||
log_print("LV snapshot status source of");
|
||||
|
||||
@@ -64,6 +64,9 @@ const char *get_alloc_string(alloc_policy_t alloc);
|
||||
char alloc_policy_char(alloc_policy_t alloc);
|
||||
alloc_policy_t get_alloc_from_string(const char *str);
|
||||
|
||||
const char *get_lock_type_string(lock_type_t lock_type);
|
||||
lock_type_t get_lock_type_from_string(const char *str);
|
||||
|
||||
const char *get_percent_string(percent_type_t def);
|
||||
|
||||
char yes_no_prompt(const char *prompt, ...) __attribute__ ((format(printf, 1, 2)));
|
||||
|
||||
@@ -164,7 +164,7 @@ int export_pv(struct cmd_context *cmd, struct dm_pool *mem __attribute__((unused
|
||||
/* Is VG already exported or being exported? */
|
||||
if (vg && vg_is_exported(vg)) {
|
||||
/* Does system_id need setting? */
|
||||
if (!*vg->lvm1_system_id ||
|
||||
if (!vg->lvm1_system_id || !*vg->lvm1_system_id ||
|
||||
strncmp(vg->lvm1_system_id, EXPORTED_TAG,
|
||||
sizeof(EXPORTED_TAG) - 1)) {
|
||||
if (!generate_lvm1_system_id(cmd, (char *)pvd->system_id, EXPORTED_TAG))
|
||||
@@ -180,7 +180,7 @@ int export_pv(struct cmd_context *cmd, struct dm_pool *mem __attribute__((unused
|
||||
}
|
||||
|
||||
/* Is VG being imported? */
|
||||
if (vg && !vg_is_exported(vg) && *vg->lvm1_system_id &&
|
||||
if (vg && !vg_is_exported(vg) && vg->lvm1_system_id && *vg->lvm1_system_id &&
|
||||
!strncmp(vg->lvm1_system_id, EXPORTED_TAG, sizeof(EXPORTED_TAG) - 1)) {
|
||||
if (!generate_lvm1_system_id(cmd, (char *)pvd->system_id, IMPORTED_TAG))
|
||||
return_0;
|
||||
@@ -192,7 +192,7 @@ int export_pv(struct cmd_context *cmd, struct dm_pool *mem __attribute__((unused
|
||||
return_0;
|
||||
|
||||
/* Update internal system_id if we changed it */
|
||||
if (vg &&
|
||||
if (vg && vg->lvm1_system_id &&
|
||||
(!*vg->lvm1_system_id ||
|
||||
strncmp(vg->lvm1_system_id, (char *)pvd->system_id, sizeof(pvd->system_id))))
|
||||
strncpy(vg->lvm1_system_id, (char *)pvd->system_id, NAME_LEN);
|
||||
|
||||
@@ -372,19 +372,61 @@ static int _print_flag_config(struct formatter *f, uint64_t status, int type)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int _out_tags(struct formatter *f, struct dm_list *tagsl)
|
||||
static char *_alloc_printed_str_list(struct dm_list *list)
|
||||
{
|
||||
char *tag_buffer;
|
||||
struct dm_str_list *sl;
|
||||
int first = 1;
|
||||
size_t size = 0;
|
||||
char *buffer, *buf;
|
||||
|
||||
if (!dm_list_empty(tagsl)) {
|
||||
if (!(tag_buffer = alloc_printed_tags(tagsl)))
|
||||
dm_list_iterate_items(sl, list)
|
||||
/* '"' + item + '"' + ',' + ' ' */
|
||||
size += strlen(sl->str) + 4;
|
||||
/* '[' + ']' + '\0' */
|
||||
size += 3;
|
||||
|
||||
if (!(buffer = buf = dm_malloc(size))) {
|
||||
log_error("Could not allocate memory for string list buffer.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!emit_to_buffer(&buf, &size, "["))
|
||||
goto_bad;
|
||||
|
||||
dm_list_iterate_items(sl, list) {
|
||||
if (!first) {
|
||||
if (!emit_to_buffer(&buf, &size, ", "))
|
||||
goto_bad;
|
||||
} else
|
||||
first = 0;
|
||||
|
||||
if (!emit_to_buffer(&buf, &size, "\"%s\"", sl->str))
|
||||
goto_bad;
|
||||
}
|
||||
|
||||
if (!emit_to_buffer(&buf, &size, "]"))
|
||||
goto_bad;
|
||||
|
||||
return buffer;
|
||||
|
||||
bad:
|
||||
dm_free(buffer);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
static int _out_list(struct formatter *f, struct dm_list *list,
|
||||
const char *list_name)
|
||||
{
|
||||
char *buffer;
|
||||
|
||||
if (!dm_list_empty(list)) {
|
||||
if (!(buffer = _alloc_printed_str_list(list)))
|
||||
return_0;
|
||||
if (!out_text(f, "tags = %s", tag_buffer)) {
|
||||
dm_free(tag_buffer);
|
||||
if (!out_text(f, "%s = %s", list_name, buffer)) {
|
||||
dm_free(buffer);
|
||||
return_0;
|
||||
}
|
||||
dm_free(tag_buffer);
|
||||
dm_free(buffer);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -422,7 +464,7 @@ static int _print_vg(struct formatter *f, struct volume_group *vg)
|
||||
if (!_print_flag_config(f, status, VG_FLAGS))
|
||||
return_0;
|
||||
|
||||
if (!_out_tags(f, &vg->tags))
|
||||
if (!_out_list(f, &vg->tags, "tags"))
|
||||
return_0;
|
||||
|
||||
if (vg->system_id && *vg->system_id)
|
||||
@@ -430,8 +472,11 @@ static int _print_vg(struct formatter *f, struct volume_group *vg)
|
||||
else if (vg->lvm1_system_id && *vg->lvm1_system_id)
|
||||
outf(f, "system_id = \"%s\"", vg->lvm1_system_id);
|
||||
|
||||
if (vg->lock_type)
|
||||
if (vg->lock_type) {
|
||||
outf(f, "lock_type = \"%s\"", vg->lock_type);
|
||||
if (vg->lock_args)
|
||||
outf(f, "lock_args = \"%s\"", vg->lock_args);
|
||||
}
|
||||
|
||||
outsize(f, (uint64_t) vg->extent_size, "extent_size = %u",
|
||||
vg->extent_size);
|
||||
@@ -509,7 +554,7 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg)
|
||||
if (!_print_flag_config(f, pv->status, PV_FLAGS))
|
||||
return_0;
|
||||
|
||||
if (!_out_tags(f, &pv->tags))
|
||||
if (!_out_list(f, &pv->tags, "tags"))
|
||||
return_0;
|
||||
|
||||
outsize(f, pv->size, "dev_size = %" PRIu64, pv->size);
|
||||
@@ -545,7 +590,7 @@ static int _print_segment(struct formatter *f, struct volume_group *vg,
|
||||
outnl(f);
|
||||
outf(f, "type = \"%s\"", seg->segtype->name);
|
||||
|
||||
if (!_out_tags(f, &seg->tags))
|
||||
if (!_out_list(f, &seg->tags, "tags"))
|
||||
return_0;
|
||||
|
||||
if (seg->segtype->ops->text_export &&
|
||||
@@ -641,7 +686,7 @@ static int _print_lv(struct formatter *f, struct logical_volume *lv)
|
||||
if (!_print_flag_config(f, status, LV_FLAGS))
|
||||
return_0;
|
||||
|
||||
if (!_out_tags(f, &lv->tags))
|
||||
if (!_out_list(f, &lv->tags, "tags"))
|
||||
return_0;
|
||||
|
||||
if (lv->timestamp) {
|
||||
@@ -657,6 +702,9 @@ static int _print_lv(struct formatter *f, struct logical_volume *lv)
|
||||
lv->timestamp);
|
||||
}
|
||||
|
||||
if (lv->lock_args)
|
||||
outf(f, "lock_args = \"%s\"", lv->lock_args);
|
||||
|
||||
if (lv->alloc != ALLOC_INHERIT)
|
||||
outf(f, "allocation_policy = \"%s\"",
|
||||
get_alloc_string(lv->alloc));
|
||||
|
||||
@@ -67,6 +67,7 @@ static const struct flag _lv_flags[] = {
|
||||
{LV_NOSCAN, NULL, 0},
|
||||
{LV_TEMPORARY, NULL, 0},
|
||||
{POOL_METADATA_SPARE, NULL, 0},
|
||||
{LOCKD_SANLOCK_LV, NULL, 0},
|
||||
{RAID, NULL, 0},
|
||||
{RAID_META, NULL, 0},
|
||||
{RAID_IMAGE, NULL, 0},
|
||||
|
||||
@@ -1603,9 +1603,9 @@ static int _text_pv_initialise(const struct format_type *fmt,
|
||||
if (rp->extent_count)
|
||||
pv->pe_count = rp->extent_count;
|
||||
|
||||
if ((pv->pe_start + pv->pe_count * pv->pe_size - 1) > (pv->size << SECTOR_SHIFT)) {
|
||||
if ((pv->pe_start + pv->pe_count * (uint64_t)pv->pe_size - 1) > pv->size) {
|
||||
log_error("Physical extents end beyond end of device %s.",
|
||||
pv_dev_name(pv));
|
||||
pv_dev_name(pv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2172,7 +2172,7 @@ static int _text_pv_add_metadata_area(const struct format_type *fmt,
|
||||
* LABEL_SCAN_SIZE.
|
||||
*/
|
||||
pe_end = pv->pe_count ? (pv->pe_start +
|
||||
pv->pe_count * pv->pe_size - 1) << SECTOR_SHIFT
|
||||
pv->pe_count * (uint64_t)pv->pe_size - 1) << SECTOR_SHIFT
|
||||
: 0;
|
||||
|
||||
if (pe_start || pe_start_locked) {
|
||||
@@ -2237,7 +2237,7 @@ static int _text_pv_add_metadata_area(const struct format_type *fmt,
|
||||
if (limit_applied)
|
||||
log_very_verbose("Using limited metadata area size on %s "
|
||||
"with value %" PRIu64 " (limited by %s of "
|
||||
"%" PRIu64 ").", pv_dev_name(pv),
|
||||
FMTu64 ").", pv_dev_name(pv),
|
||||
mda_size, limit_name, limit);
|
||||
|
||||
if (mda_size) {
|
||||
@@ -2491,7 +2491,7 @@ struct format_type *create_text_format(struct cmd_context *cmd)
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if ((cn = find_config_tree_node(cmd, metadata_dirs_CFG, NULL))) {
|
||||
if ((cn = find_config_tree_array(cmd, metadata_dirs_CFG, NULL))) {
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
if (cv->type != DM_CFG_STRING) {
|
||||
log_error("Invalid string in config file: "
|
||||
|
||||
@@ -47,7 +47,8 @@ struct text_vg_version_ops {
|
||||
int (*check_version) (const struct dm_config_tree * cf);
|
||||
struct volume_group *(*read_vg) (struct format_instance * fid,
|
||||
const struct dm_config_tree *cf,
|
||||
unsigned use_cached_pvs);
|
||||
unsigned use_cached_pvs,
|
||||
unsigned allow_lvmetad_extensions);
|
||||
void (*read_desc) (struct dm_pool * mem, const struct dm_config_tree *cf,
|
||||
time_t *when, char **desc);
|
||||
int (*read_vgname) (const struct format_type *fmt,
|
||||
@@ -60,9 +61,6 @@ struct text_vg_version_ops *text_vg_vsn1_init(void);
|
||||
int print_flags(uint64_t status, int type, char *buffer, size_t size);
|
||||
int read_flags(uint64_t *status, int type, const struct dm_config_value *cv);
|
||||
|
||||
char *alloc_printed_tags(struct dm_list *tags);
|
||||
int read_tags(struct dm_pool *mem, struct dm_list *tags, const struct dm_config_value *cv);
|
||||
|
||||
int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp);
|
||||
size_t text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf);
|
||||
struct volume_group *text_vg_import_file(struct format_instance *fid,
|
||||
|
||||
@@ -146,7 +146,7 @@ struct volume_group *text_vg_import_fd(struct format_instance *fid,
|
||||
if (!(*vsn)->check_version(cft))
|
||||
continue;
|
||||
|
||||
if (!(vg = (*vsn)->read_vg(fid, cft, single_device)))
|
||||
if (!(vg = (*vsn)->read_vg(fid, cft, single_device, 0)))
|
||||
goto_out;
|
||||
|
||||
(*vsn)->read_desc(vg->vgmem, cft, when, desc);
|
||||
@@ -174,8 +174,9 @@ struct volume_group *text_vg_import_file(struct format_instance *fid,
|
||||
when, desc);
|
||||
}
|
||||
|
||||
struct volume_group *import_vg_from_config_tree(const struct dm_config_tree *cft,
|
||||
struct format_instance *fid)
|
||||
static struct volume_group *_import_vg_from_config_tree(const struct dm_config_tree *cft,
|
||||
struct format_instance *fid,
|
||||
unsigned allow_lvmetad_extensions)
|
||||
{
|
||||
struct volume_group *vg = NULL;
|
||||
struct text_vg_version_ops **vsn;
|
||||
@@ -190,7 +191,7 @@ struct volume_group *import_vg_from_config_tree(const struct dm_config_tree *cft
|
||||
* The only path to this point uses cached vgmetadata,
|
||||
* so it can use cached PV state too.
|
||||
*/
|
||||
if (!(vg = (*vsn)->read_vg(fid, cft, 1)))
|
||||
if (!(vg = (*vsn)->read_vg(fid, cft, 1, allow_lvmetad_extensions)))
|
||||
stack;
|
||||
else if ((vg_missing = vg_missing_pv_count(vg))) {
|
||||
log_verbose("There are %d physical volumes missing.",
|
||||
@@ -203,3 +204,15 @@ struct volume_group *import_vg_from_config_tree(const struct dm_config_tree *cft
|
||||
|
||||
return vg;
|
||||
}
|
||||
|
||||
struct volume_group *import_vg_from_config_tree(const struct dm_config_tree *cft,
|
||||
struct format_instance *fid)
|
||||
{
|
||||
return _import_vg_from_config_tree(cft, fid, 0);
|
||||
}
|
||||
|
||||
struct volume_group *import_vg_from_lvmetad_config_tree(const struct dm_config_tree *cft,
|
||||
struct format_instance *fid)
|
||||
{
|
||||
return _import_vg_from_config_tree(cft, fid, 1);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
#include "toolcontext.h"
|
||||
#include "lvmcache.h"
|
||||
#include "lvmetad.h"
|
||||
#include "lvmlockd.h"
|
||||
#include "lv_alloc.h"
|
||||
#include "pv_alloc.h"
|
||||
#include "segtype.h"
|
||||
#include "text_import.h"
|
||||
#include "defaults.h"
|
||||
#include "str_list.h"
|
||||
|
||||
typedef int (*section_fn) (struct format_instance * fid,
|
||||
struct volume_group * vg, const struct dm_config_node * pvn,
|
||||
@@ -153,6 +155,26 @@ static int _read_flag_config(const struct dm_config_node *n, uint64_t *status, i
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_str_list(struct dm_pool *mem, struct dm_list *list, const struct dm_config_value *cv)
|
||||
{
|
||||
if (cv->type == DM_CFG_EMPTY_ARRAY)
|
||||
return 1;
|
||||
|
||||
while (cv) {
|
||||
if (cv->type != DM_CFG_STRING) {
|
||||
log_error("Found an item that is not a string");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!str_list_add(mem, list, dm_pool_strdup(mem, cv->v.str)))
|
||||
return_0;
|
||||
|
||||
cv = cv->next;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_pv(struct format_instance *fid,
|
||||
struct volume_group *vg, const struct dm_config_node *pvn,
|
||||
const struct dm_config_node *vgn __attribute__((unused)),
|
||||
@@ -167,6 +189,8 @@ static int _read_pv(struct format_instance *fid,
|
||||
const struct dm_config_value *cv;
|
||||
uint64_t size, ba_start;
|
||||
|
||||
int outdated = !strcmp(pvn->parent->key, "outdated_pvs");
|
||||
|
||||
if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) ||
|
||||
!(pvl->pv = dm_pool_zalloc(mem, sizeof(*pvl->pv))))
|
||||
return_0;
|
||||
@@ -212,7 +236,7 @@ static int _read_pv(struct format_instance *fid,
|
||||
|
||||
memcpy(&pv->vgid, &vg->id, sizeof(vg->id));
|
||||
|
||||
if (!_read_flag_config(pvn, &pv->status, PV_FLAGS)) {
|
||||
if (!outdated && !_read_flag_config(pvn, &pv->status, PV_FLAGS)) {
|
||||
log_error("Couldn't read status flags for physical volume.");
|
||||
return 0;
|
||||
}
|
||||
@@ -234,13 +258,13 @@ static int _read_pv(struct format_instance *fid,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_read_uint64(pvn, "pe_start", &pv->pe_start)) {
|
||||
if (!outdated && !_read_uint64(pvn, "pe_start", &pv->pe_start)) {
|
||||
log_error("Couldn't read extent start value (pe_start) "
|
||||
"for physical volume.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_read_int32(pvn, "pe_count", &pv->pe_count)) {
|
||||
if (!outdated && !_read_int32(pvn, "pe_count", &pv->pe_count)) {
|
||||
log_error("Couldn't find extent count (pe_count) for "
|
||||
"physical volume.");
|
||||
return 0;
|
||||
@@ -251,7 +275,7 @@ static int _read_pv(struct format_instance *fid,
|
||||
_read_uint64(pvn, "ba_start", &ba_start);
|
||||
_read_uint64(pvn, "ba_size", &size);
|
||||
if (ba_start && size) {
|
||||
log_debug("Found bootloader area specification for PV %s "
|
||||
log_debug_metadata("Found bootloader area specification for PV %s "
|
||||
"in metadata: ba_start=%" PRIu64 ", ba_size=%" PRIu64 ".",
|
||||
pv_dev_name(pv), ba_start, size);
|
||||
pv->ba_start = ba_start;
|
||||
@@ -267,7 +291,7 @@ static int _read_pv(struct format_instance *fid,
|
||||
|
||||
/* Optional tags */
|
||||
if (dm_config_get_list(pvn, "tags", &cv) &&
|
||||
!(read_tags(mem, &pv->tags, cv))) {
|
||||
!(_read_str_list(mem, &pv->tags, cv))) {
|
||||
log_error("Couldn't read tags for physical volume %s in %s.",
|
||||
pv_dev_name(pv), vg->name);
|
||||
return 0;
|
||||
@@ -299,7 +323,10 @@ static int _read_pv(struct format_instance *fid,
|
||||
|
||||
vg->extent_count += pv->pe_count;
|
||||
vg->free_count += pv->pe_count;
|
||||
add_pvl_to_vgs(vg, pvl);
|
||||
if (outdated)
|
||||
dm_list_add(&vg->pvs_outdated, &pvl->list);
|
||||
else
|
||||
add_pvl_to_vgs(vg, pvl);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -375,7 +402,7 @@ static int _read_segment(struct logical_volume *lv, const struct dm_config_node
|
||||
|
||||
/* Optional tags */
|
||||
if (dm_config_get_list(sn_child, "tags", &cv) &&
|
||||
!(read_tags(mem, &seg->tags, cv))) {
|
||||
!(_read_str_list(mem, &seg->tags, cv))) {
|
||||
log_error("Couldn't read tags for a segment of %s/%s.",
|
||||
lv->vg->name, lv->name);
|
||||
return 0;
|
||||
@@ -573,6 +600,30 @@ static int _read_lvnames(struct format_instance *fid __attribute__((unused)),
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The LV lock_args string is generated in lvmlockd, and the content
|
||||
* depends on the lock_type.
|
||||
*
|
||||
* lock_type dlm does not use LV lock_args, so the LV lock_args field
|
||||
* is just set to "dlm".
|
||||
*
|
||||
* lock_type sanlock uses the LV lock_args field to save the
|
||||
* location on disk of that LV's sanlock lock. The disk name is
|
||||
* specified in the VG lock_args. The lock_args string begins
|
||||
* with a version number, e.g. 1.0.0, followed by a colon, followed
|
||||
* by a number. The number is the offset on disk where sanlock is
|
||||
* told to find the LV's lock.
|
||||
* e.g. lock_args = 1.0.0:70254592
|
||||
* means that the lock is located at offset 70254592.
|
||||
*
|
||||
* The lvmlockd code for each specific lock manager also validates
|
||||
* the lock_args before using it to access the lock manager.
|
||||
*/
|
||||
if (dm_config_get_str(lvn, "lock_args", &str)) {
|
||||
if (!(lv->lock_args = dm_pool_strdup(mem, str)))
|
||||
return_0;
|
||||
}
|
||||
|
||||
lv->alloc = ALLOC_INHERIT;
|
||||
if (dm_config_get_str(lvn, "allocation_policy", &str)) {
|
||||
lv->alloc = get_alloc_from_string(str);
|
||||
@@ -611,7 +662,7 @@ static int _read_lvnames(struct format_instance *fid __attribute__((unused)),
|
||||
|
||||
/* Optional tags */
|
||||
if (dm_config_get_list(lvn, "tags", &cv) &&
|
||||
!(read_tags(mem, &lv->tags, cv))) {
|
||||
!(_read_str_list(mem, &lv->tags, cv))) {
|
||||
log_error("Couldn't read tags for logical volume %s/%s.",
|
||||
vg->name, lv->name);
|
||||
return 0;
|
||||
@@ -638,6 +689,12 @@ static int _read_lvnames(struct format_instance *fid __attribute__((unused)),
|
||||
vg->pool_metadata_spare_lv = lv;
|
||||
}
|
||||
|
||||
if (!lv_is_visible(lv) && !strcmp(lv->name, LOCKD_SANLOCK_LV_NAME)) {
|
||||
log_debug_metadata("Logical volume %s is sanlock lv.", lv->name);
|
||||
lv->status |= LOCKD_SANLOCK_LV;
|
||||
vg->sanlock_lv = lv;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -735,7 +792,8 @@ static int _read_sections(struct format_instance *fid,
|
||||
|
||||
static struct volume_group *_read_vg(struct format_instance *fid,
|
||||
const struct dm_config_tree *cft,
|
||||
unsigned use_cached_pvs)
|
||||
unsigned use_cached_pvs,
|
||||
unsigned allow_lvmetad_extensions)
|
||||
{
|
||||
const struct dm_config_node *vgn;
|
||||
const struct dm_config_value *cv;
|
||||
@@ -789,6 +847,32 @@ static struct volume_group *_read_vg(struct format_instance *fid,
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* The VG lock_args string is generated in lvmlockd, and the content
|
||||
* depends on the lock_type. lvmlockd begins the lock_args string
|
||||
* with a version number, e.g. 1.0.0, followed by a colon, followed
|
||||
* by a string that depends on the lock manager. The string after
|
||||
* the colon is information needed to use the lock manager for the VG.
|
||||
*
|
||||
* For sanlock, the string is the name of the internal LV used to store
|
||||
* sanlock locks. lvmlockd needs to know where the locks are located
|
||||
* so it can pass that location to sanlock which needs to access the locks.
|
||||
* e.g. lock_args = 1.0.0:lvmlock
|
||||
* means that the locks are located on the the LV "lvmlock".
|
||||
*
|
||||
* For dlm, the string is the dlm cluster name. lvmlockd needs to use
|
||||
* a dlm lockspace in this cluster to use the VG.
|
||||
* e.g. lock_args = 1.0.0:foo
|
||||
* means that the host needs to be a member of the cluster "foo".
|
||||
*
|
||||
* The lvmlockd code for each specific lock manager also validates
|
||||
* the lock_args before using it to access the lock manager.
|
||||
*/
|
||||
if (dm_config_get_str(vgn, "lock_args", &str)) {
|
||||
if (!(vg->lock_args = dm_pool_strdup(vg->vgmem, str)))
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_id(&vg->id, vgn, "id")) {
|
||||
log_error("Couldn't read uuid for volume group %s.", vg->name);
|
||||
goto bad;
|
||||
@@ -877,9 +961,15 @@ static struct volume_group *_read_vg(struct format_instance *fid,
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (allow_lvmetad_extensions)
|
||||
_read_sections(fid, "outdated_pvs", _read_pv, vg,
|
||||
vgn, pv_hash, lv_hash, 1, &scan_done_once);
|
||||
else if (dm_config_has_node(vgn, "outdated_pvs"))
|
||||
log_error(INTERNAL_ERROR "Unexpected outdated_pvs section in metadata of VG %s.", vg->name);
|
||||
|
||||
/* Optional tags */
|
||||
if (dm_config_get_list(vgn, "tags", &cv) &&
|
||||
!(read_tags(vg->vgmem, &vg->tags, cv))) {
|
||||
!(_read_str_list(vg->vgmem, &vg->tags, cv))) {
|
||||
log_error("Couldn't read tags for volume group %s.", vg->name);
|
||||
goto bad;
|
||||
}
|
||||
@@ -941,6 +1031,11 @@ static void _read_desc(struct dm_pool *mem,
|
||||
*when = u;
|
||||
}
|
||||
|
||||
/*
|
||||
* It would be more accurate to call this _read_vgsummary().
|
||||
* It is used to read vgsummary information about a VG
|
||||
* before locking and reading the VG via vg_read().
|
||||
*/
|
||||
static int _read_vgname(const struct format_type *fmt, const struct dm_config_tree *cft,
|
||||
struct lvmcache_vgsummary *vgsummary)
|
||||
{
|
||||
@@ -977,6 +1072,8 @@ static int _read_vgname(const struct format_type *fmt, const struct dm_config_tr
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_config_get_str(vgn, "lock_type", &vgsummary->lock_type);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "metadata.h"
|
||||
#include "import-export.h"
|
||||
#include "str_list.h"
|
||||
#include "lvm-string.h"
|
||||
|
||||
char *alloc_printed_tags(struct dm_list *tagsl)
|
||||
{
|
||||
struct dm_str_list *sl;
|
||||
int first = 1;
|
||||
size_t size = 0;
|
||||
char *buffer, *buf;
|
||||
|
||||
dm_list_iterate_items(sl, tagsl)
|
||||
/* '"' + tag + '"' + ',' + ' ' */
|
||||
size += strlen(sl->str) + 4;
|
||||
/* '[' + ']' + '\0' */
|
||||
size += 3;
|
||||
|
||||
if (!(buffer = buf = dm_malloc(size))) {
|
||||
log_error("Could not allocate memory for tag list buffer.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!emit_to_buffer(&buf, &size, "["))
|
||||
goto_bad;
|
||||
|
||||
dm_list_iterate_items(sl, tagsl) {
|
||||
if (!first) {
|
||||
if (!emit_to_buffer(&buf, &size, ", "))
|
||||
goto_bad;
|
||||
} else
|
||||
first = 0;
|
||||
|
||||
if (!emit_to_buffer(&buf, &size, "\"%s\"", sl->str))
|
||||
goto_bad;
|
||||
}
|
||||
|
||||
if (!emit_to_buffer(&buf, &size, "]"))
|
||||
goto_bad;
|
||||
|
||||
return buffer;
|
||||
|
||||
bad:
|
||||
dm_free(buffer);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
int read_tags(struct dm_pool *mem, struct dm_list *tagsl, const struct dm_config_value *cv)
|
||||
{
|
||||
if (cv->type == DM_CFG_EMPTY_ARRAY)
|
||||
return 1;
|
||||
|
||||
while (cv) {
|
||||
if (cv->type != DM_CFG_STRING) {
|
||||
log_error("Found a tag that is not a string");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!str_list_add(mem, tagsl, dm_pool_strdup(mem, cv->v.str)))
|
||||
return_0;
|
||||
|
||||
cv = cv->next;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -418,8 +418,8 @@ static int _text_read(struct labeller *l, struct device *dev, void *buf,
|
||||
if (!(ext_version = xlate32(pvhdr_ext->version)))
|
||||
goto out;
|
||||
|
||||
log_debug("%s: PV header extension version %" PRIu32 " found",
|
||||
dev_name(dev), ext_version);
|
||||
log_debug_metadata("%s: PV header extension version %" PRIu32 " found",
|
||||
dev_name(dev), ext_version);
|
||||
|
||||
/* Bootloader areas */
|
||||
dlocn_xl = pvhdr_ext->bootloader_areas_xl;
|
||||
|
||||
@@ -119,8 +119,9 @@ int init_locking(int type, struct cmd_context *cmd, int suppress_messages)
|
||||
switch (type) {
|
||||
case 0:
|
||||
init_no_locking(&_locking, cmd, suppress_messages);
|
||||
log_warn("WARNING: Locking disabled. Be careful! "
|
||||
"This could corrupt your metadata.");
|
||||
log_warn_suppress(suppress_messages,
|
||||
"WARNING: Locking disabled. Be careful! "
|
||||
"This could corrupt your metadata.");
|
||||
return 1;
|
||||
|
||||
case 1:
|
||||
|
||||
@@ -195,9 +195,10 @@ int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname);
|
||||
|
||||
#define unlock_vg(cmd, vol) \
|
||||
do { \
|
||||
if (is_real_vg(vol)) \
|
||||
sync_dev_names(cmd); \
|
||||
(void) lock_vol(cmd, vol, LCK_VG_UNLOCK, NULL); \
|
||||
if (is_real_vg(vol) && !sync_dev_names(cmd)) \
|
||||
stack; \
|
||||
if (!lock_vol(cmd, vol, LCK_VG_UNLOCK, NULL)) \
|
||||
stack; \
|
||||
} while (0)
|
||||
#define unlock_and_release_vg(cmd, vg, vol) \
|
||||
do { \
|
||||
|
||||
2604
lib/locking/lvmlockd.c
Normal file
2604
lib/locking/lvmlockd.c
Normal file
File diff suppressed because it is too large
Load Diff
246
lib/locking/lvmlockd.h
Normal file
246
lib/locking/lvmlockd.h
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _LVMLOCKD_H
|
||||
#define _LVMLOCKD_H
|
||||
|
||||
#include "config-util.h"
|
||||
#include "daemon-client.h"
|
||||
|
||||
#define LOCKD_SANLOCK_LV_NAME "lvmlock"
|
||||
|
||||
/* lockd_gl flags */
|
||||
#define LDGL_UPDATE_NAMES 0x00000001
|
||||
|
||||
/* lockd_lv flags */
|
||||
#define LDLV_MODE_NO_SH 0x00000001
|
||||
#define LDLV_PERSISTENT 0x00000002
|
||||
|
||||
/* lvmlockd result flags */
|
||||
#define LD_RF_NO_LOCKSPACES 0x00000001
|
||||
#define LD_RF_NO_GL_LS 0x00000002
|
||||
#define LD_RF_WARN_GL_REMOVED 0x00000004
|
||||
#define LD_RF_DUP_GL_LS 0x00000008
|
||||
#define LD_RF_INACTIVE_LS 0x00000010
|
||||
#define LD_RF_ADD_LS_ERROR 0x00000020
|
||||
|
||||
/* lockd_state flags */
|
||||
#define LDST_EX 0x00000001
|
||||
#define LDST_SH 0x00000002
|
||||
#define LDST_FAIL_REQUEST 0x00000004
|
||||
#define LDST_FAIL_NOLS 0x00000008
|
||||
#define LDST_FAIL_STARTING 0x00000010
|
||||
#define LDST_FAIL_OTHER 0x00000020
|
||||
#define LDST_FAIL (LDST_FAIL_REQUEST | LDST_FAIL_NOLS | LDST_FAIL_STARTING | LDST_FAIL_OTHER)
|
||||
|
||||
#ifdef LVMLOCKD_SUPPORT
|
||||
|
||||
/* lvmlockd connection and communication */
|
||||
|
||||
void lvmlockd_set_socket(const char *sock);
|
||||
void lvmlockd_set_use(int use);
|
||||
int lvmlockd_use(void);
|
||||
void lvmlockd_init(struct cmd_context *cmd);
|
||||
void lvmlockd_connect(void);
|
||||
void lvmlockd_disconnect(void);
|
||||
|
||||
/* vgcreate/vgremove use init/free */
|
||||
|
||||
int lockd_init_vg(struct cmd_context *cmd, struct volume_group *vg, const char *lock_type, int lv_lock_count);
|
||||
int lockd_free_vg_before(struct cmd_context *cmd, struct volume_group *vg);
|
||||
void lockd_free_vg_final(struct cmd_context *cmd, struct volume_group *vg);
|
||||
|
||||
/* vgrename */
|
||||
|
||||
int lockd_rename_vg_before(struct cmd_context *cmd, struct volume_group *vg);
|
||||
int lockd_rename_vg_final(struct cmd_context *cmd, struct volume_group *vg, int success);
|
||||
|
||||
/* start and stop the lockspace for a vg */
|
||||
|
||||
int lockd_start_vg(struct cmd_context *cmd, struct volume_group *vg);
|
||||
int lockd_stop_vg(struct cmd_context *cmd, struct volume_group *vg);
|
||||
int lockd_start_wait(struct cmd_context *cmd);
|
||||
|
||||
/* locking */
|
||||
|
||||
int lockd_gl_create(struct cmd_context *cmd, const char *def_mode, const char *vg_lock_type);
|
||||
int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags);
|
||||
int lockd_vg(struct cmd_context *cmd, const char *vg_name, const char *def_mode,
|
||||
uint32_t flags, uint32_t *lockd_state);
|
||||
int lockd_vg_update(struct volume_group *vg);
|
||||
|
||||
int lockd_lv_name(struct cmd_context *cmd, struct volume_group *vg,
|
||||
const char *lv_name, struct id *lv_id,
|
||||
const char *lock_args, const char *def_mode, uint32_t flags);
|
||||
int lockd_lv(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
const char *def_mode, uint32_t flags);
|
||||
|
||||
/* lvcreate/lvremove use init/free */
|
||||
|
||||
int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg, struct logical_volume *lv,
|
||||
struct lvcreate_params *lp);
|
||||
int lockd_init_lv_args(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct logical_volume *lv, const char *lock_type, const char **lock_args);
|
||||
int lockd_free_lv(struct cmd_context *cmd, struct volume_group *vg,
|
||||
const char *lv_name, struct id *lv_id, const char *lock_args);
|
||||
|
||||
const char *lockd_running_lock_type(struct cmd_context *cmd);
|
||||
|
||||
int handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg);
|
||||
|
||||
int lockd_lv_uses_lock(struct logical_volume *lv);
|
||||
|
||||
#else /* LVMLOCKD_SUPPORT */
|
||||
|
||||
static inline void lvmlockd_set_socket(const char *sock)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void lvmlockd_set_use(int use)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void lvmlockd_init(struct cmd_context *cmd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void lvmlockd_disconnect(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void lvmlockd_connect(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int lvmlockd_use(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int lockd_init_vg(struct cmd_context *cmd, struct volume_group *vg, const char *lock_type, int lv_lock_count)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_free_vg_before(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void lockd_free_vg_final(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int lockd_rename_vg_before(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_rename_vg_final(struct cmd_context *cmd, struct volume_group *vg, int success)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_start_vg(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int lockd_stop_vg(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int lockd_start_wait(struct cmd_context *cmd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int lockd_gl_create(struct cmd_context *cmd, const char *def_mode, const char *vg_lock_type)
|
||||
{
|
||||
/*
|
||||
* When lvm is built without lvmlockd support, creating a VG with
|
||||
* a shared lock type should fail.
|
||||
*/
|
||||
if (is_lockd_type(vg_lock_type)) {
|
||||
log_error("Using a shared lock type requires lvmlockd.");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_vg(struct cmd_context *cmd, const char *vg_name, const char *def_mode,
|
||||
uint32_t flags, uint32_t *lockd_state)
|
||||
{
|
||||
*lockd_state = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_vg_update(struct volume_group *vg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_lv_name(struct cmd_context *cmd, struct volume_group *vg,
|
||||
const char *lv_name, struct id *lv_id,
|
||||
const char *lock_args, const char *def_mode, uint32_t flags)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_lv(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
const char *def_mode, uint32_t flags)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct logical_volume *lv, struct lvcreate_params *lp)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_init_lv_args(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct logical_volume *lv, const char *lock_type, const char **lock_args)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int lockd_free_lv(struct cmd_context *cmd, struct volume_group *vg,
|
||||
const char *lv_name, struct id *lv_id, const char *lock_args)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline const char *lockd_running_lock_type(struct cmd_context *cmd)
|
||||
{
|
||||
log_error("Using a shared lock type requires lvmlockd.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int lockd_lv_uses_lock(struct logical_volume *lv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* LVMLOCKD_SUPPORT */
|
||||
|
||||
#endif /* _LVMLOCKD_H */
|
||||
@@ -36,7 +36,7 @@ static int _indent = 1;
|
||||
static int _log_suppress = 0;
|
||||
static char _msg_prefix[30] = " ";
|
||||
static int _already_logging = 0;
|
||||
static int _abort_on_internal_errors = 0;
|
||||
static int _abort_on_internal_errors_config = 0;
|
||||
|
||||
static lvm2_log_fn_t _lvm2_log_fn = NULL;
|
||||
|
||||
@@ -69,7 +69,7 @@ void init_log_file(const char *log_file, int append)
|
||||
static const char statfile[] = "/proc/self/stat";
|
||||
const char *env;
|
||||
int pid;
|
||||
long long starttime;
|
||||
unsigned long long starttime;
|
||||
FILE *st;
|
||||
int i = 0;
|
||||
|
||||
@@ -92,9 +92,6 @@ void init_log_file(const char *log_file, int append)
|
||||
"%llu", &pid, &starttime) != 2) {
|
||||
log_warn("WARNING: Cannot parse content of %s.", statfile);
|
||||
} else {
|
||||
if (fclose(st))
|
||||
log_sys_debug("fclose", statfile);
|
||||
|
||||
if (dm_snprintf(_log_file_path, sizeof(_log_file_path),
|
||||
"%s_%s_%d_%lld", log_file, env, pid, starttime) < 0) {
|
||||
log_warn("WARNING: Debug log file path is too long for epoch.");
|
||||
@@ -104,7 +101,11 @@ void init_log_file(const char *log_file, int append)
|
||||
append = 1; /* force */
|
||||
}
|
||||
}
|
||||
|
||||
if (st && fclose(st))
|
||||
log_sys_debug("fclose", statfile);
|
||||
}
|
||||
|
||||
no_epoch:
|
||||
if (!(_log_file = fopen(log_file, append ? "a" : "w"))) {
|
||||
log_sys_error("fopen", log_file);
|
||||
@@ -217,9 +218,10 @@ void init_indent(int indent)
|
||||
_indent = indent;
|
||||
}
|
||||
|
||||
/* If present, environment setting will override this. */
|
||||
void init_abort_on_internal_errors(int fatal)
|
||||
{
|
||||
_abort_on_internal_errors = fatal;
|
||||
_abort_on_internal_errors_config = fatal;
|
||||
}
|
||||
|
||||
void reset_lvm_errno(int store_errmsg)
|
||||
@@ -276,10 +278,24 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
size_t msglen;
|
||||
const char *indent_spaces = "";
|
||||
FILE *stream;
|
||||
static int _abort_on_internal_errors_env_present = -1;
|
||||
static int _abort_on_internal_errors_env = 0;
|
||||
char *env_str;
|
||||
|
||||
level &= ~(_LOG_STDERR|_LOG_ONCE);
|
||||
|
||||
if (_abort_on_internal_errors &&
|
||||
if (_abort_on_internal_errors_env_present < 0) {
|
||||
if ((env_str = getenv("DM_ABORT_ON_INTERNAL_ERRORS"))) {
|
||||
_abort_on_internal_errors_env_present = 1;
|
||||
/* Set when env DM_ABORT_ON_INTERNAL_ERRORS is not "0" */
|
||||
_abort_on_internal_errors_env = strcmp(env_str, "0");
|
||||
} else
|
||||
_abort_on_internal_errors_env_present = 0;
|
||||
}
|
||||
|
||||
/* Use value from environment if present, otherwise use value from config. */
|
||||
if (((_abort_on_internal_errors_env_present && _abort_on_internal_errors_env) ||
|
||||
(!_abort_on_internal_errors_env_present && _abort_on_internal_errors_config)) &&
|
||||
!strncmp(format, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1)) {
|
||||
fatal_internal_error = 1;
|
||||
/* Internal errors triggering abort cannot be suppressed. */
|
||||
@@ -385,6 +401,8 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
default:
|
||||
/* Typically only log_warn goes to stdout */
|
||||
stream = (use_stderr || (level != _LOG_WARN)) ? stderr : stdout;
|
||||
if (stream == stderr)
|
||||
fflush(stdout);
|
||||
fprintf(stream, "%s%s%s%s", buf, log_command_name(),
|
||||
_msg_prefix, indent_spaces);
|
||||
vfprintf(stream, trformat, ap);
|
||||
|
||||
@@ -37,8 +37,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* FILE */
|
||||
#include <string.h> /* strerror() */
|
||||
#include <errno.h>
|
||||
|
||||
#define EUNCLASSIFIED -1 /* Generic error code */
|
||||
@@ -67,6 +65,7 @@
|
||||
#define LOG_CLASS_METADATA 0x0020 /* "metadata" */
|
||||
#define LOG_CLASS_CACHE 0x0040 /* "cache" */
|
||||
#define LOG_CLASS_LOCKING 0x0080 /* "locking" */
|
||||
#define LOG_CLASS_LVMPOLLD 0x0100 /* "lvmpolld" */
|
||||
|
||||
#define log_debug(x...) LOG_LINE(_LOG_DEBUG, x)
|
||||
#define log_debug_mem(x...) LOG_LINE_WITH_CLASS(_LOG_DEBUG, LOG_CLASS_MEM, x)
|
||||
@@ -77,6 +76,7 @@
|
||||
#define log_debug_metadata(x...) LOG_LINE_WITH_CLASS(_LOG_DEBUG, LOG_CLASS_METADATA, x)
|
||||
#define log_debug_cache(x...) LOG_LINE_WITH_CLASS(_LOG_DEBUG, LOG_CLASS_CACHE, x)
|
||||
#define log_debug_locking(x...) LOG_LINE_WITH_CLASS(_LOG_DEBUG, LOG_CLASS_LOCKING, x)
|
||||
#define log_debug_lvmpolld(x...) LOG_LINE_WITH_CLASS(_LOG_DEBUG, LOG_CLASS_LVMPOLLD, x)
|
||||
|
||||
#define log_info(x...) LOG_LINE(_LOG_INFO, x)
|
||||
#define log_notice(x...) LOG_LINE(_LOG_NOTICE, x)
|
||||
|
||||
356
lib/lvmpolld/lvmpolld-client.c
Normal file
356
lib/lvmpolld/lvmpolld-client.c
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Red Hat, Inc.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
|
||||
#include "daemon-io.h"
|
||||
#include "lvmpolld-client.h"
|
||||
#include "lvmpolld-protocol.h"
|
||||
#include "metadata-exported.h"
|
||||
#include "polldaemon.h"
|
||||
#include "toolcontext.h"
|
||||
#include "lvm2cmd.h"
|
||||
|
||||
struct progress_info {
|
||||
unsigned error:1;
|
||||
unsigned finished:1;
|
||||
int cmd_signal;
|
||||
int cmd_retcode;
|
||||
};
|
||||
|
||||
static int _lvmpolld_use;
|
||||
static int _lvmpolld_connected;
|
||||
static const char* _lvmpolld_socket;
|
||||
|
||||
static daemon_handle _lvmpolld = { .error = 0 };
|
||||
|
||||
static daemon_handle _lvmpolld_open(const char *socket)
|
||||
{
|
||||
daemon_info lvmpolld_info = {
|
||||
.path = "lvmpolld",
|
||||
.socket = socket ?: LVMPOLLD_SOCKET,
|
||||
.protocol = LVMPOLLD_PROTOCOL,
|
||||
.protocol_version = LVMPOLLD_PROTOCOL_VERSION
|
||||
};
|
||||
|
||||
return daemon_open(lvmpolld_info);
|
||||
}
|
||||
|
||||
void lvmpolld_set_active(int active)
|
||||
{
|
||||
_lvmpolld_use = active;
|
||||
}
|
||||
|
||||
void lvmpolld_set_socket(const char *socket)
|
||||
{
|
||||
_lvmpolld_socket = socket;
|
||||
}
|
||||
|
||||
static void _lvmpolld_connect_or_warn(void)
|
||||
{
|
||||
if (!_lvmpolld_connected && !_lvmpolld.error) {
|
||||
_lvmpolld = _lvmpolld_open(_lvmpolld_socket);
|
||||
if ( _lvmpolld.socket_fd >= 0 && !_lvmpolld.error) {
|
||||
log_debug_lvmpolld("Sucessfully connected to lvmpolld on fd %d.", _lvmpolld.socket_fd);
|
||||
_lvmpolld_connected = 1;
|
||||
} else {
|
||||
log_warn("WARNING: Failed to connect to lvmpolld. Proceeding with polling without using lvmpolld.");
|
||||
log_warn("WARNING: Check global/use_lvmpolld in lvm.conf or the lvmpolld daemon state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lvmpolld_use(void)
|
||||
{
|
||||
if (!_lvmpolld_use || !_lvmpolld_socket)
|
||||
return 0;
|
||||
|
||||
_lvmpolld_connect_or_warn();
|
||||
|
||||
return _lvmpolld_connected;
|
||||
}
|
||||
|
||||
void lvmpolld_disconnect(void)
|
||||
{
|
||||
if (_lvmpolld_connected) {
|
||||
daemon_close(_lvmpolld);
|
||||
_lvmpolld_connected = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void _explain_error_codes(int retcode)
|
||||
{
|
||||
switch (retcode) {
|
||||
/* LVM2 return codes */
|
||||
case LVM2_NO_SUCH_COMMAND:
|
||||
log_error("LVM command run by lvmpolld responded with: 'No such command.'");
|
||||
break;
|
||||
case LVM2_INVALID_PARAMETERS:
|
||||
log_error("LVM command run by lvmpolld failed due to invalid parameters.");
|
||||
break;
|
||||
case LVM2_PROCESSING_FAILED:
|
||||
log_error("LVM command executed by lvmpolld failed.");
|
||||
break;
|
||||
|
||||
/* lvmpolld specific return codes */
|
||||
case LVMPD_RET_DUP_FAILED:
|
||||
log_error("lvmpolld failed to duplicate file descriptors.");
|
||||
/* fall through */
|
||||
case LVMPD_RET_EXC_FAILED:
|
||||
log_error("lvmpolld failed to exec() lvm binary.");
|
||||
break;
|
||||
default:
|
||||
log_error("lvmpolld responded with unexpected return code.");
|
||||
}
|
||||
|
||||
log_print_unless_silent("For more information see lvmpolld messages in syslog or lvmpolld log file.");
|
||||
}
|
||||
|
||||
static void _process_error_response(daemon_reply rep)
|
||||
{
|
||||
if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_FAILED))
|
||||
log_error("lvmpolld failed to process a request. The reason was: %s.",
|
||||
daemon_reply_str(rep, "reason", "<empty>"));
|
||||
else if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_EINVAL))
|
||||
log_error("lvmpolld couldn't handle a request. "
|
||||
"It might be due to daemon internal state. The reason was: %s.",
|
||||
daemon_reply_str(rep, "reason", "<empty>"));
|
||||
else
|
||||
log_error("Unexpected response %s. The reason: %s.",
|
||||
daemon_reply_str(rep, "response", "<empty>"),
|
||||
daemon_reply_str(rep, "reason", "<empty>"));
|
||||
|
||||
log_print_unless_silent("For more information see lvmpolld messages in syslog or lvmpolld log file.");
|
||||
}
|
||||
|
||||
static struct progress_info _request_progress_info(const char *uuid, unsigned abort_polling)
|
||||
{
|
||||
daemon_reply rep;
|
||||
const char *e = getenv("LVM_SYSTEM_DIR");
|
||||
struct progress_info ret = { .error = 1, .finished = 1 };
|
||||
daemon_request req = daemon_request_make(LVMPD_REQ_PROGRESS);
|
||||
|
||||
if (!daemon_request_extend(req, LVMPD_PARM_LVID " = %s", uuid, NULL)) {
|
||||
log_error("Failed to create " LVMPD_REQ_PROGRESS " request.");
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
if (abort_polling &&
|
||||
!daemon_request_extend(req, LVMPD_PARM_ABORT " = %d", (int64_t)abort_polling, NULL)) {
|
||||
log_error("Failed to create " LVMPD_REQ_PROGRESS " request.");
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
if (e &&
|
||||
!(daemon_request_extend(req, LVMPD_PARM_SYSDIR " = %s",
|
||||
e, NULL))) {
|
||||
log_error("Failed to create " LVMPD_REQ_PROGRESS " request.");
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
rep = daemon_send(_lvmpolld, req);
|
||||
if (rep.error) {
|
||||
log_error("Failed to process request with error %s (errno: %d).",
|
||||
strerror(rep.error), rep.error);
|
||||
goto out_rep;
|
||||
}
|
||||
|
||||
if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_IN_PROGRESS)) {
|
||||
ret.finished = 0;
|
||||
ret.error = 0;
|
||||
} else if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_FINISHED)) {
|
||||
if (!strcmp(daemon_reply_str(rep, "reason", ""), LVMPD_REAS_SIGNAL))
|
||||
ret.cmd_signal = daemon_reply_int(rep, LVMPD_PARM_VALUE, 0);
|
||||
else
|
||||
ret.cmd_retcode = daemon_reply_int(rep, LVMPD_PARM_VALUE, -1);
|
||||
ret.error = 0;
|
||||
} else if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_NOT_FOUND)) {
|
||||
log_verbose("No polling operation in progress regarding LV %s.", uuid);
|
||||
ret.error = 0;
|
||||
} else {
|
||||
_process_error_response(rep);
|
||||
stack;
|
||||
}
|
||||
|
||||
out_rep:
|
||||
daemon_reply_destroy(rep);
|
||||
out_req:
|
||||
daemon_request_destroy(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* interval in seconds long
|
||||
* enough for more than a year
|
||||
* of waiting
|
||||
*/
|
||||
#define INTERV_SIZE 10
|
||||
|
||||
static int _process_poll_init(const struct cmd_context *cmd, const char *poll_type,
|
||||
const struct poll_operation_id *id, const struct daemon_parms *parms)
|
||||
{
|
||||
char *str;
|
||||
daemon_reply rep;
|
||||
daemon_request req;
|
||||
const char *e = getenv("LVM_SYSTEM_DIR");
|
||||
int r = 0;
|
||||
|
||||
str = dm_malloc(INTERV_SIZE * sizeof(char));
|
||||
if (!str)
|
||||
return r;
|
||||
|
||||
if (snprintf(str, INTERV_SIZE, "%u", parms->interval) >= INTERV_SIZE) {
|
||||
log_warn("Interval string conversion got truncated.");
|
||||
str[INTERV_SIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
req = daemon_request_make(poll_type);
|
||||
if (!daemon_request_extend(req, LVMPD_PARM_LVID " = %s", id->uuid,
|
||||
LVMPD_PARM_VGNAME " = %s", id->vg_name,
|
||||
LVMPD_PARM_LVNAME " = %s", id->lv_name,
|
||||
LVMPD_PARM_INTERVAL " = %s", str,
|
||||
"cmdline = %s", cmd->cmd_line, /* FIXME: debug param only */
|
||||
NULL)) {
|
||||
log_error("Failed to create %s request.", poll_type);
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
if (parms->aborting &&
|
||||
!(daemon_request_extend(req, LVMPD_PARM_ABORT " = %d", (int64_t)(parms->aborting), NULL))) {
|
||||
log_error("Failed to create %s request." , poll_type);
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
if (cmd->handles_missing_pvs &&
|
||||
!(daemon_request_extend(req, LVMPD_PARM_HANDLE_MISSING_PVS " = %d",
|
||||
(int64_t)(cmd->handles_missing_pvs), NULL))) {
|
||||
log_error("Failed to create %s request." , poll_type);
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
if (e &&
|
||||
!(daemon_request_extend(req, LVMPD_PARM_SYSDIR " = %s",
|
||||
e, NULL))) {
|
||||
log_error("Failed to create %s request." , poll_type);
|
||||
goto out_req;
|
||||
}
|
||||
|
||||
rep = daemon_send(_lvmpolld, req);
|
||||
|
||||
if (rep.error) {
|
||||
log_error("Failed to process request with error %s (errno: %d).",
|
||||
strerror(rep.error), rep.error);
|
||||
goto out_rep;
|
||||
}
|
||||
|
||||
if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_OK))
|
||||
r = 1;
|
||||
else {
|
||||
_process_error_response(rep);
|
||||
stack;
|
||||
}
|
||||
|
||||
out_rep:
|
||||
daemon_reply_destroy(rep);
|
||||
out_req:
|
||||
daemon_request_destroy(req);
|
||||
dm_free(str);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int lvmpolld_poll_init(const struct cmd_context *cmd, const struct poll_operation_id *id,
|
||||
const struct daemon_parms *parms)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if (!id->uuid) {
|
||||
log_error(INTERNAL_ERROR "Use of lvmpolld requires uuid set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!id->vg_name) {
|
||||
log_error(INTERNAL_ERROR "Use of lvmpolld requires vgname set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!id->lv_name) {
|
||||
log_error(INTERNAL_ERROR "Use of lvmpolld requires lvname set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parms->lv_type & PVMOVE) {
|
||||
log_debug_lvmpolld("Asking lvmpolld for pvmove%s on %s/%s.",
|
||||
parms->aborting ? " abort" : "", id->vg_name, id->lv_name);
|
||||
r = _process_poll_init(cmd, LVMPD_REQ_PVMOVE, id, parms);
|
||||
} else if (parms->lv_type & CONVERTING) {
|
||||
log_debug_lvmpolld("Asking lvmpolld for mirror conversion on %s/%s.",
|
||||
id->vg_name, id->lv_name);
|
||||
r = _process_poll_init(cmd, LVMPD_REQ_CONVERT, id, parms);
|
||||
} else if (parms->lv_type & MERGING) {
|
||||
if (parms->lv_type & SNAPSHOT) {
|
||||
log_debug_lvmpolld("Asking lvmpolld for snapshot merge on %s/%s.",
|
||||
id->vg_name, id->lv_name);
|
||||
r = _process_poll_init(cmd, LVMPD_REQ_MERGE, id, parms);
|
||||
}
|
||||
else if (parms->lv_type & THIN_VOLUME) {
|
||||
log_debug_lvmpolld("Asking lvmpolld for thin snapshot merge on %s/%s.",
|
||||
id->vg_name, id->lv_name);
|
||||
r = _process_poll_init(cmd, LVMPD_REQ_MERGE_THIN, id, parms);
|
||||
}
|
||||
else {
|
||||
log_error(INTERNAL_ERROR "Unsupported poll operation.");
|
||||
}
|
||||
} else
|
||||
log_error(INTERNAL_ERROR "Unsupported poll operation");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int lvmpolld_request_info(const struct poll_operation_id *id, const struct daemon_parms *parms, unsigned *finished)
|
||||
{
|
||||
struct progress_info info;
|
||||
int ret = 0;
|
||||
|
||||
*finished = 1;
|
||||
|
||||
if (!id->uuid) {
|
||||
log_error(INTERNAL_ERROR "use of lvmpolld requires uuid being set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug_lvmpolld("Asking lvmpolld for progress status of an operation on %s/%s.",
|
||||
id->vg_name, id->lv_name);
|
||||
info = _request_progress_info(id->uuid, parms->aborting);
|
||||
*finished = info.finished;
|
||||
|
||||
if (info.error)
|
||||
return_0;
|
||||
|
||||
if (info.finished) {
|
||||
if (info.cmd_signal)
|
||||
log_error("Command executed by lvmpolld got terminated by signal (%d).",
|
||||
info.cmd_signal);
|
||||
else if (info.cmd_retcode)
|
||||
_explain_error_codes(info.cmd_retcode);
|
||||
else {
|
||||
log_verbose("Polling finished successfully.");
|
||||
ret = 1;
|
||||
}
|
||||
} else
|
||||
ret = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
52
lib/lvmpolld/lvmpolld-client.h
Normal file
52
lib/lvmpolld/lvmpolld-client.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LVMPOLLD_CLIENT_H
|
||||
#define _LVM_LVMPOLLD_CLIENT_H
|
||||
# ifdef LVMPOLLD_SUPPORT
|
||||
|
||||
# include "daemon-client.h"
|
||||
|
||||
# define LVMPOLLD_SOCKET DEFAULT_RUN_DIR "/lvmpolld.socket"
|
||||
|
||||
struct cmd_context;
|
||||
struct poll_operation_id;
|
||||
struct daemon_parms;
|
||||
|
||||
void lvmpolld_disconnect(void);
|
||||
|
||||
int lvmpolld_poll_init(const struct cmd_context *cmd, const struct poll_operation_id *id,
|
||||
const struct daemon_parms *parms);
|
||||
|
||||
int lvmpolld_request_info(const struct poll_operation_id *id, const struct daemon_parms *parms,
|
||||
unsigned *finished);
|
||||
|
||||
int lvmpolld_use(void);
|
||||
|
||||
void lvmpolld_set_active(int active);
|
||||
|
||||
void lvmpolld_set_socket(const char *socket);
|
||||
|
||||
# else
|
||||
|
||||
# define lvmpolld_disconnect() do {} while (0)
|
||||
# define lvmpolld_poll_init(cmd, id, parms) (0)
|
||||
# define lvmpolld_request_info(id, parms, finished) (0)
|
||||
# define lvmpolld_use() (0)
|
||||
# define lvmpolld_set_active(active) do {} while (0)
|
||||
# define lvmpolld_set_socket(socket) do {} while (0)
|
||||
|
||||
# endif /* LVMPOLLD_SUPPORT */
|
||||
|
||||
#endif /* _LVM_LVMPOLLD_CLIENT_H */
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -29,14 +29,6 @@ struct daemon_parms;
|
||||
|
||||
struct poll_functions {
|
||||
const char *(*get_copy_name_from_lv) (const struct logical_volume *lv);
|
||||
struct volume_group *(*get_copy_vg) (struct cmd_context *cmd,
|
||||
const char *name,
|
||||
const char *uuid);
|
||||
struct logical_volume *(*get_copy_lv) (struct cmd_context *cmd,
|
||||
struct volume_group *vg,
|
||||
const char *name,
|
||||
const char *uuid,
|
||||
uint64_t lv_type);
|
||||
progress_t (*poll_progress)(struct cmd_context *cmd,
|
||||
struct logical_volume *lv,
|
||||
const char *name,
|
||||
@@ -51,6 +43,13 @@ struct poll_functions {
|
||||
struct dm_list *lvs_changed);
|
||||
};
|
||||
|
||||
struct poll_operation_id {
|
||||
const char *vg_name;
|
||||
const char *lv_name;
|
||||
const char *display_name;
|
||||
const char *uuid;
|
||||
};
|
||||
|
||||
struct daemon_parms {
|
||||
unsigned interval;
|
||||
unsigned wait_before_testing;
|
||||
@@ -63,13 +62,15 @@ struct daemon_parms {
|
||||
struct poll_functions *poll_fns;
|
||||
};
|
||||
|
||||
int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid,
|
||||
unsigned background,
|
||||
int poll_daemon(struct cmd_context *cmd, unsigned background,
|
||||
uint64_t lv_type, struct poll_functions *poll_fns,
|
||||
const char *progress_title);
|
||||
const char *progress_title, struct poll_operation_id *id);
|
||||
|
||||
progress_t poll_mirror_progress(struct cmd_context *cmd,
|
||||
struct logical_volume *lv, const char *name,
|
||||
struct daemon_parms *parms);
|
||||
|
||||
int wait_for_single_lv(struct cmd_context *cmd, struct poll_operation_id *id,
|
||||
struct daemon_parms *parms);
|
||||
|
||||
#endif
|
||||
@@ -29,7 +29,17 @@
|
||||
#define DM_HINT_OVERHEAD_PER_BLOCK 8 /* bytes */
|
||||
#define DM_MAX_HINT_WIDTH (4+16) /* bytes. FIXME Configurable? */
|
||||
|
||||
const char *get_cache_pool_cachemode_name(const struct lv_segment *seg)
|
||||
int cache_mode_is_set(const struct lv_segment *seg)
|
||||
{
|
||||
if (seg_is_cache(seg))
|
||||
seg = first_seg(seg->pool_lv);
|
||||
|
||||
return (seg->feature_flags & (DM_CACHE_FEATURE_WRITEBACK |
|
||||
DM_CACHE_FEATURE_WRITETHROUGH |
|
||||
DM_CACHE_FEATURE_PASSTHROUGH)) ? 1 : 0;
|
||||
}
|
||||
|
||||
const char *get_cache_mode_name(const struct lv_segment *seg)
|
||||
{
|
||||
if (seg->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
|
||||
return "writeback";
|
||||
@@ -46,19 +56,48 @@ const char *get_cache_pool_cachemode_name(const struct lv_segment *seg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int set_cache_pool_feature(uint64_t *feature_flags, const char *str)
|
||||
int cache_set_mode(struct lv_segment *seg, const char *str)
|
||||
{
|
||||
struct cmd_context *cmd = seg->lv->vg->cmd;
|
||||
int id;
|
||||
uint64_t mode;
|
||||
|
||||
if (!str && !seg_is_cache(seg))
|
||||
return 1; /* Defaults only for cache */
|
||||
|
||||
if (seg_is_cache(seg))
|
||||
seg = first_seg(seg->pool_lv);
|
||||
|
||||
if (!str) {
|
||||
if (cache_mode_is_set(seg))
|
||||
return 1; /* Default already set in cache pool */
|
||||
|
||||
id = allocation_cache_mode_CFG;
|
||||
|
||||
/* If present, check backward compatible settings */
|
||||
if (!find_config_node(cmd, cmd->cft, id) &&
|
||||
find_config_node(cmd, cmd->cft, allocation_cache_pool_cachemode_CFG))
|
||||
id = allocation_cache_pool_cachemode_CFG;
|
||||
|
||||
str = find_config_tree_str(cmd, id, NULL);
|
||||
}
|
||||
|
||||
if (!strcmp(str, "writeback"))
|
||||
*feature_flags |= DM_CACHE_FEATURE_WRITEBACK;
|
||||
mode = DM_CACHE_FEATURE_WRITEBACK;
|
||||
else if (!strcmp(str, "writethrough"))
|
||||
*feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH;
|
||||
else if (!strcmp(str, "passhrough"))
|
||||
*feature_flags |= DM_CACHE_FEATURE_PASSTHROUGH;
|
||||
mode = DM_CACHE_FEATURE_WRITETHROUGH;
|
||||
else if (!strcmp(str, "passthrough"))
|
||||
mode = DM_CACHE_FEATURE_PASSTHROUGH;
|
||||
else {
|
||||
log_error("Cache pool feature \"%s\" is unknown.", str);
|
||||
log_error("Cannot set unknown cache mode \"%s\".", str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
seg->feature_flags &= ~(DM_CACHE_FEATURE_WRITEBACK |
|
||||
DM_CACHE_FEATURE_WRITETHROUGH |
|
||||
DM_CACHE_FEATURE_PASSTHROUGH);
|
||||
seg->feature_flags |= mode;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -322,7 +361,7 @@ int lv_cache_remove(struct logical_volume *cache_lv)
|
||||
dirty_blocks = status->cache->dirty_blocks;
|
||||
dm_pool_destroy(status->mem);
|
||||
if (dirty_blocks) {
|
||||
log_print_unless_silent("%" PRIu64 " blocks must still be flushed.",
|
||||
log_print_unless_silent(FMTu64 " blocks must still be flushed.",
|
||||
dirty_blocks);
|
||||
sleep(1);
|
||||
}
|
||||
@@ -395,37 +434,74 @@ int lv_is_cache_origin(const struct logical_volume *lv)
|
||||
return seg && lv_is_cache(seg->lv) && !lv_is_pending_delete(seg->lv) && (seg_lv(seg, 0) == lv);
|
||||
}
|
||||
|
||||
int lv_cache_setpolicy(struct logical_volume *lv, struct dm_config_tree *policy)
|
||||
int cache_set_policy(struct lv_segment *seg, const char *name,
|
||||
const struct dm_config_tree *settings)
|
||||
{
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
const char *name;
|
||||
struct dm_config_node *cn;
|
||||
const struct dm_config_node *cns;
|
||||
struct dm_config_tree *old = NULL, *new = NULL, *tmp = NULL;
|
||||
int r = 0;
|
||||
const int passed_seg_is_cache = seg_is_cache(seg);
|
||||
|
||||
if (lv_is_cache(lv))
|
||||
if (passed_seg_is_cache)
|
||||
seg = first_seg(seg->pool_lv);
|
||||
|
||||
if (seg->policy_settings) {
|
||||
if (!(old = dm_config_create()))
|
||||
goto_out;
|
||||
if (!(new = dm_config_create()))
|
||||
goto_out;
|
||||
new->root = policy->root;
|
||||
old->root = seg->policy_settings;
|
||||
new->cascade = old;
|
||||
if (!(tmp = policy = dm_config_flatten(new)))
|
||||
if (name) {
|
||||
if (!(seg->policy_name = dm_pool_strdup(seg->lv->vg->vgmem, name))) {
|
||||
log_error("Failed to duplicate policy name.");
|
||||
return 0;
|
||||
}
|
||||
} else if (!seg->policy_name && passed_seg_is_cache)
|
||||
seg->policy_name = find_config_tree_str(seg->lv->vg->cmd, allocation_cache_policy_CFG, NULL);
|
||||
|
||||
if (settings) {
|
||||
if (!seg->policy_name) {
|
||||
log_error(INTERNAL_ERROR "Can't set policy settings without policy name.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (seg->policy_settings) {
|
||||
if (!(old = dm_config_create()))
|
||||
goto_out;
|
||||
if (!(new = dm_config_create()))
|
||||
goto_out;
|
||||
new->root = settings->root;
|
||||
old->root = seg->policy_settings;
|
||||
new->cascade = old;
|
||||
if (!(tmp = dm_config_flatten(new)))
|
||||
goto_out;
|
||||
}
|
||||
|
||||
if ((cn = dm_config_find_node((tmp) ? tmp->root : settings->root, "policy_settings")) &&
|
||||
!(seg->policy_settings = dm_config_clone_node_with_mem(seg->lv->vg->vgmem, cn, 0)))
|
||||
goto_out;
|
||||
} else if (passed_seg_is_cache && /* Look for command's profile cache_policies */
|
||||
(cns = find_config_tree_node(seg->lv->vg->cmd, allocation_cache_settings_CFG_SECTION, NULL))) {
|
||||
/* Try to find our section for given policy */
|
||||
for (cn = cns->child; cn; cn = cn->sib) {
|
||||
/* Only matching section names */
|
||||
if (cn->v || strcmp(cn->key, seg->policy_name) != 0)
|
||||
continue;
|
||||
|
||||
if (!cn->child)
|
||||
break;
|
||||
|
||||
if (!(new = dm_config_create()))
|
||||
goto_out;
|
||||
|
||||
if (!(new->root = dm_config_clone_node_with_mem(new->mem,
|
||||
cn->child, 1)))
|
||||
goto_out;
|
||||
|
||||
if (!(seg->policy_settings = dm_config_create_node(new, "policy_settings")))
|
||||
goto_out;
|
||||
|
||||
seg->policy_settings->child = new->root;
|
||||
|
||||
break; /* Only first match counts */
|
||||
}
|
||||
}
|
||||
|
||||
if ((cn = dm_config_find_node(policy->root, "policy_settings")) &&
|
||||
!(seg->policy_settings = dm_config_clone_node_with_mem(lv->vg->vgmem, cn, 0)))
|
||||
goto_out;
|
||||
|
||||
if ((name = dm_config_find_str(policy->root, "policy", NULL)) &&
|
||||
!(seg->policy_name = dm_pool_strdup(lv->vg->vgmem, name)))
|
||||
goto_out;
|
||||
|
||||
restart: /* remove any 'default" nodes */
|
||||
cn = seg->policy_settings ? seg->policy_settings->child : NULL;
|
||||
while (cn) {
|
||||
@@ -439,12 +515,13 @@ restart: /* remove any 'default" nodes */
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
if (old)
|
||||
dm_config_destroy(old);
|
||||
if (new)
|
||||
dm_config_destroy(new);
|
||||
if (tmp)
|
||||
dm_config_destroy(tmp);
|
||||
if (new)
|
||||
dm_config_destroy(new);
|
||||
if (old)
|
||||
dm_config_destroy(old);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "toolcontext.h"
|
||||
#include "segtype.h"
|
||||
#include "str_list.h"
|
||||
#include "lvmlockd.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/utsname.h>
|
||||
@@ -78,7 +79,7 @@ static char *_format_pvsegs(struct dm_pool *mem, const struct lv_segment *seg,
|
||||
|
||||
if (range_format) {
|
||||
if (dm_snprintf(extent_str, sizeof(extent_str),
|
||||
"%" PRIu32, extent + seg->area_len - 1) < 0) {
|
||||
FMTu32, extent + seg->area_len - 1) < 0) {
|
||||
log_error("Extent number dm_snprintf failed");
|
||||
return NULL;
|
||||
}
|
||||
@@ -130,7 +131,7 @@ char *lvseg_discards_dup(struct dm_pool *mem, const struct lv_segment *seg)
|
||||
|
||||
char *lvseg_cachemode_dup(struct dm_pool *mem, const struct lv_segment *seg)
|
||||
{
|
||||
const char *name = get_cache_pool_cachemode_name(seg);
|
||||
const char *name = get_cache_mode_name(seg);
|
||||
|
||||
if (!name)
|
||||
return_NULL;
|
||||
@@ -873,17 +874,16 @@ int lv_set_creation(struct logical_volume *lv,
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *lv_time_dup(struct dm_pool *mem, const struct logical_volume *lv)
|
||||
char *lv_time_dup(struct dm_pool *mem, const struct logical_volume *lv, int iso_mode)
|
||||
{
|
||||
char buffer[50];
|
||||
char buffer[4096];
|
||||
struct tm *local_tm;
|
||||
time_t ts = (time_t)lv->timestamp;
|
||||
const char *format = iso_mode ? DEFAULT_TIME_FORMAT : lv->vg->cmd->time_format;
|
||||
|
||||
if (!ts ||
|
||||
!(local_tm = localtime(&ts)) ||
|
||||
/* FIXME: make this lvm.conf configurable */
|
||||
!strftime(buffer, sizeof(buffer),
|
||||
"%Y-%m-%d %T %z", local_tm))
|
||||
!strftime(buffer, sizeof(buffer), format, local_tm))
|
||||
buffer[0] = 0;
|
||||
|
||||
return dm_pool_strdup(mem, buffer);
|
||||
@@ -911,6 +911,19 @@ static int _lv_is_exclusive(struct logical_volume *lv)
|
||||
int lv_active_change(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
enum activation_change activate, int needs_exclusive)
|
||||
{
|
||||
const char *ay_with_mode = NULL;
|
||||
|
||||
if (activate == CHANGE_ASY)
|
||||
ay_with_mode = "sh";
|
||||
if (activate == CHANGE_AEY)
|
||||
ay_with_mode = "ex";
|
||||
|
||||
if (is_change_activating(activate) &&
|
||||
!lockd_lv(cmd, lv, ay_with_mode, LDLV_PERSISTENT)) {
|
||||
log_error("Failed to lock logical volume %s/%s", lv->vg->name, lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (activate) {
|
||||
case CHANGE_AN:
|
||||
deactivate:
|
||||
@@ -953,7 +966,9 @@ exclusive:
|
||||
if (!activate_lv_excl(cmd, lv))
|
||||
return_0;
|
||||
break;
|
||||
default: /* CHANGE_AY */
|
||||
case CHANGE_ASY:
|
||||
case CHANGE_AY:
|
||||
default:
|
||||
if (needs_exclusive || _lv_is_exclusive(lv))
|
||||
goto exclusive;
|
||||
log_verbose("Activating logical volume \"%s\".", lv->name);
|
||||
@@ -961,6 +976,10 @@ exclusive:
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (!is_change_activating(activate) &&
|
||||
!lockd_lv(cmd, lv, "un", LDLV_PERSISTENT))
|
||||
log_error("Failed to unlock logical volume %s/%s", lv->vg->name, lv->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1000,6 +1019,12 @@ char *lv_profile_dup(struct dm_pool *mem, const struct logical_volume *lv)
|
||||
return dm_pool_strdup(mem, profile_name);
|
||||
}
|
||||
|
||||
char *lv_lock_args_dup(struct dm_pool *mem, const struct logical_volume *lv)
|
||||
{
|
||||
const char *lock_args = lv->lock_args ? lv->lock_args : "";
|
||||
return dm_pool_strdup(mem, lock_args);
|
||||
}
|
||||
|
||||
/* For given LV find recursively the LV which holds lock for it */
|
||||
const struct logical_volume *lv_lock_holder(const struct logical_volume *lv)
|
||||
{
|
||||
|
||||
@@ -51,7 +51,9 @@ struct logical_volume {
|
||||
struct dm_list segs_using_this_lv;
|
||||
|
||||
uint64_t timestamp;
|
||||
unsigned new_lock_args:1;
|
||||
const char *hostname;
|
||||
const char *lock_args;
|
||||
};
|
||||
|
||||
struct lv_with_info_and_seg_status;
|
||||
@@ -91,7 +93,7 @@ char *lvseg_monitor_dup(struct dm_pool *mem, const struct lv_segment *seg);
|
||||
char *lvseg_tags_dup(const struct lv_segment *seg);
|
||||
char *lvseg_devices(struct dm_pool *mem, const struct lv_segment *seg);
|
||||
char *lvseg_seg_pe_ranges(struct dm_pool *mem, const struct lv_segment *seg);
|
||||
char *lv_time_dup(struct dm_pool *mem, const struct logical_volume *lv);
|
||||
char *lv_time_dup(struct dm_pool *mem, const struct logical_volume *lv, int iso_mode);
|
||||
char *lv_host_dup(struct dm_pool *mem, const struct logical_volume *lv);
|
||||
int lv_set_creation(struct logical_volume *lv,
|
||||
const char *hostname, uint64_t timestamp);
|
||||
@@ -103,6 +105,7 @@ const struct logical_volume *lv_lock_holder(const struct logical_volume *lv);
|
||||
const struct logical_volume *lv_ondisk(const struct logical_volume *lv);
|
||||
struct profile *lv_config_profile(const struct logical_volume *lv);
|
||||
char *lv_profile_dup(struct dm_pool *mem, const struct logical_volume *lv);
|
||||
char *lv_lock_args_dup(struct dm_pool *mem, const struct logical_volume *lv);
|
||||
int lv_mirror_image_in_sync(const struct logical_volume *lv);
|
||||
int lv_raid_image_in_sync(const struct logical_volume *lv);
|
||||
int lv_raid_healthy(const struct logical_volume *lv);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "lvm-exec.h"
|
||||
#include "lvm-signal.h"
|
||||
#include "memlock.h"
|
||||
#include "lvmlockd.h"
|
||||
|
||||
typedef enum {
|
||||
PREFERRED,
|
||||
@@ -73,6 +74,7 @@ struct alloc_state {
|
||||
uint32_t areas_size;
|
||||
uint32_t log_area_count_still_needed; /* Number of areas still needing to be allocated for the log */
|
||||
uint32_t allocated; /* Total number of extents allocated so far */
|
||||
uint32_t num_positional_areas; /* Number of parallel allocations that must be contiguous/cling */
|
||||
};
|
||||
|
||||
struct lv_names {
|
||||
@@ -774,9 +776,6 @@ int get_default_region_size(struct cmd_context *cmd)
|
||||
{
|
||||
int region_size = _get_default_region_size(cmd);
|
||||
|
||||
if (region_size > INT32_MAX)
|
||||
region_size = INT32_MAX;
|
||||
|
||||
if (region_size & (region_size - 1)) {
|
||||
region_size = _round_down_pow2(region_size);
|
||||
log_verbose("Reducing mirror region size to %u kiB (power of 2).",
|
||||
@@ -1398,6 +1397,28 @@ int replace_lv_with_error_segment(struct logical_volume *lv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lv_refresh_suspend_resume(struct cmd_context *cmd, struct logical_volume *lv)
|
||||
{
|
||||
if (!cmd->partial_activation && (lv->status & PARTIAL_LV)) {
|
||||
log_error("Refusing refresh of partial LV %s."
|
||||
" Use '--activationmode partial' to override.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!suspend_lv(cmd, lv)) {
|
||||
log_error("Failed to suspend %s.", display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!resume_lv(cmd, lv)) {
|
||||
log_error("Failed to reactivate %s.", display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove given number of extents from LV.
|
||||
*/
|
||||
@@ -2222,6 +2243,10 @@ static int _is_condition(struct cmd_context *cmd __attribute__((unused)),
|
||||
if (!pvmatch->condition(pvmatch, pvseg, pvmatch->pva))
|
||||
return 1; /* Continue */
|
||||
|
||||
if (positional && (s >= pvmatch->alloc_state->num_positional_areas))
|
||||
return 1;
|
||||
|
||||
/* FIXME The previous test should make this one redundant. */
|
||||
if (positional && (s >= pvmatch->alloc_state->areas_size))
|
||||
return 1;
|
||||
|
||||
@@ -2459,6 +2484,8 @@ static void _clear_areas(struct alloc_state *alloc_state)
|
||||
{
|
||||
uint32_t s;
|
||||
|
||||
alloc_state->num_positional_areas = 0;
|
||||
|
||||
for (s = 0; s < alloc_state->areas_size; s++)
|
||||
alloc_state->areas[s].pva = NULL;
|
||||
}
|
||||
@@ -2501,9 +2528,10 @@ static void _report_needed_allocation_space(struct alloc_handle *ah,
|
||||
metadata_count = alloc_state->log_area_count_still_needed;
|
||||
}
|
||||
|
||||
log_debug_alloc("Still need %s%" PRIu32 " total extents from %" PRIu32 " remaining:",
|
||||
log_debug_alloc("Still need %s%" PRIu32 " total extents from %" PRIu32 " remaining (%" PRIu32 " positional slots):",
|
||||
ah->approx_alloc ? "up to " : "",
|
||||
parallel_area_size * parallel_areas_count + metadata_size * metadata_count, pv_maps_size(pvms));
|
||||
parallel_area_size * parallel_areas_count + metadata_size * metadata_count, pv_maps_size(pvms),
|
||||
alloc_state->num_positional_areas);
|
||||
log_debug_alloc(" %" PRIu32 " (%" PRIu32 " data/%" PRIu32
|
||||
" parity) parallel areas of %" PRIu32 " extents each",
|
||||
parallel_areas_count, ah->area_count, ah->parity_count, parallel_area_size);
|
||||
@@ -2558,7 +2586,6 @@ static int _find_some_parallel_space(struct alloc_handle *ah,
|
||||
struct pv_area *pva;
|
||||
unsigned preferred_count = 0;
|
||||
unsigned already_found_one;
|
||||
unsigned ix_offset = 0; /* Offset for non-preferred allocations */
|
||||
unsigned ix_log_offset; /* Offset to start of areas to use for log */
|
||||
unsigned too_small_for_log_count; /* How many too small for log? */
|
||||
unsigned iteration_count = 0; /* cling_to_alloced may need 2 iterations */
|
||||
@@ -2568,27 +2595,28 @@ static int _find_some_parallel_space(struct alloc_handle *ah,
|
||||
uint32_t devices_needed = ah->area_count + ah->parity_count;
|
||||
uint32_t required;
|
||||
|
||||
/* ix_offset holds the number of parallel allocations that must be contiguous/cling */
|
||||
_clear_areas(alloc_state);
|
||||
_reset_unreserved(pvms);
|
||||
|
||||
/* num_positional_areas holds the number of parallel allocations that must be contiguous/cling */
|
||||
/* These appear first in the array, so it is also the offset to the non-preferred allocations */
|
||||
/* At most one of A_CONTIGUOUS_TO_LVSEG, A_CLING_TO_LVSEG or A_CLING_TO_ALLOCED may be set */
|
||||
if (!(alloc_parms->flags & A_POSITIONAL_FILL))
|
||||
ix_offset = 0;
|
||||
alloc_state->num_positional_areas = 0;
|
||||
else if (alloc_parms->flags & (A_CONTIGUOUS_TO_LVSEG | A_CLING_TO_LVSEG))
|
||||
ix_offset = _stripes_per_mimage(alloc_parms->prev_lvseg) * alloc_parms->prev_lvseg->area_count;
|
||||
alloc_state->num_positional_areas = _stripes_per_mimage(alloc_parms->prev_lvseg) * alloc_parms->prev_lvseg->area_count;
|
||||
else if (alloc_parms->flags & A_CLING_TO_ALLOCED)
|
||||
ix_offset = ah->area_count;
|
||||
alloc_state->num_positional_areas = ah->area_count;
|
||||
|
||||
if (alloc_parms->alloc == ALLOC_NORMAL || (alloc_parms->flags & A_CLING_TO_ALLOCED))
|
||||
log_debug_alloc("Cling_to_allocated is %sset",
|
||||
alloc_parms->flags & A_CLING_TO_ALLOCED ? "" : "not ");
|
||||
|
||||
if (alloc_parms->flags & A_POSITIONAL_FILL)
|
||||
log_debug_alloc("%u preferred area(s) to be filled positionally.", ix_offset);
|
||||
log_debug_alloc("%u preferred area(s) to be filled positionally.", alloc_state->num_positional_areas);
|
||||
else
|
||||
log_debug_alloc("Areas to be sorted and filled sequentially.");
|
||||
|
||||
_clear_areas(alloc_state);
|
||||
_reset_unreserved(pvms);
|
||||
|
||||
_report_needed_allocation_space(ah, alloc_state, pvms);
|
||||
|
||||
/* ix holds the number of areas found on other PVs */
|
||||
@@ -2596,7 +2624,7 @@ static int _find_some_parallel_space(struct alloc_handle *ah,
|
||||
if (log_iteration_count) {
|
||||
log_debug_alloc("Found %u areas for %" PRIu32 " parallel areas and %" PRIu32 " log areas so far.", ix, devices_needed, alloc_state->log_area_count_still_needed);
|
||||
} else if (iteration_count)
|
||||
log_debug_alloc("Filled %u out of %u preferred areas so far.", preferred_count, ix_offset);
|
||||
log_debug_alloc("Filled %u out of %u preferred areas so far.", preferred_count, alloc_state->num_positional_areas);
|
||||
|
||||
/*
|
||||
* Provide for escape from the loop if no progress is made.
|
||||
@@ -2637,7 +2665,7 @@ static int _find_some_parallel_space(struct alloc_handle *ah,
|
||||
* not enough for the logs.
|
||||
*/
|
||||
if (log_iteration_count) {
|
||||
for (s = devices_needed; s < ix + ix_offset; s++)
|
||||
for (s = devices_needed; s < ix + alloc_state->num_positional_areas; s++)
|
||||
if (alloc_state->areas[s].pva && alloc_state->areas[s].pva->map->pv == pvm->pv)
|
||||
goto next_pv;
|
||||
/* On a second pass, avoid PVs already used in an uncommitted area */
|
||||
@@ -2685,8 +2713,8 @@ static int _find_some_parallel_space(struct alloc_handle *ah,
|
||||
}
|
||||
|
||||
/* Reserve required amount of pva */
|
||||
required = _calc_required_extents(ah, pva, ix + ix_offset - 1, max_to_allocate, alloc_parms->alloc);
|
||||
if (!_reserve_required_area(ah, alloc_state, pva, required, ix + ix_offset - 1, pva->unreserved))
|
||||
required = _calc_required_extents(ah, pva, ix + alloc_state->num_positional_areas - 1, max_to_allocate, alloc_parms->alloc);
|
||||
if (!_reserve_required_area(ah, alloc_state, pva, required, ix + alloc_state->num_positional_areas - 1, pva->unreserved))
|
||||
return_0;
|
||||
}
|
||||
|
||||
@@ -2697,23 +2725,23 @@ static int _find_some_parallel_space(struct alloc_handle *ah,
|
||||
/* With cling and contiguous we stop if we found a match for *all* the areas */
|
||||
/* FIXME Rename these variables! */
|
||||
if ((alloc_parms->alloc == ALLOC_ANYWHERE &&
|
||||
ix + ix_offset >= devices_needed + alloc_state->log_area_count_still_needed) ||
|
||||
(preferred_count == ix_offset &&
|
||||
(ix_offset == devices_needed + alloc_state->log_area_count_still_needed)))
|
||||
ix + alloc_state->num_positional_areas >= devices_needed + alloc_state->log_area_count_still_needed) ||
|
||||
(preferred_count == alloc_state->num_positional_areas &&
|
||||
(alloc_state->num_positional_areas == devices_needed + alloc_state->log_area_count_still_needed)))
|
||||
break;
|
||||
}
|
||||
} while ((alloc_parms->alloc == ALLOC_ANYWHERE && last_ix != ix && ix < devices_needed + alloc_state->log_area_count_still_needed) ||
|
||||
/* With cling_to_alloced and normal, if there were gaps in the preferred areas, have a second iteration */
|
||||
(alloc_parms->alloc == ALLOC_NORMAL && preferred_count &&
|
||||
(preferred_count < ix_offset || alloc_state->log_area_count_still_needed) &&
|
||||
(preferred_count < alloc_state->num_positional_areas || alloc_state->log_area_count_still_needed) &&
|
||||
(alloc_parms->flags & A_CLING_TO_ALLOCED) && !iteration_count++) ||
|
||||
/* Extra iteration needed to fill log areas on PVs already used? */
|
||||
(alloc_parms->alloc == ALLOC_NORMAL && preferred_count == ix_offset && !ah->mirror_logs_separate &&
|
||||
(alloc_parms->alloc == ALLOC_NORMAL && preferred_count == alloc_state->num_positional_areas && !ah->mirror_logs_separate &&
|
||||
(ix + preferred_count >= devices_needed) &&
|
||||
(ix + preferred_count < devices_needed + alloc_state->log_area_count_still_needed) && !log_iteration_count++));
|
||||
|
||||
/* Non-zero ix means at least one USE_AREA was returned */
|
||||
if (preferred_count < ix_offset && !(alloc_parms->flags & A_CLING_TO_ALLOCED) && !ix)
|
||||
if (preferred_count < alloc_state->num_positional_areas && !(alloc_parms->flags & A_CLING_TO_ALLOCED) && !ix)
|
||||
return 1;
|
||||
|
||||
if (ix + preferred_count < devices_needed + alloc_state->log_area_count_still_needed)
|
||||
@@ -2728,17 +2756,17 @@ static int _find_some_parallel_space(struct alloc_handle *ah,
|
||||
}
|
||||
} else if (ix > 1) {
|
||||
log_debug_alloc("Sorting %u areas", ix);
|
||||
qsort(alloc_state->areas + ix_offset, ix, sizeof(*alloc_state->areas),
|
||||
qsort(alloc_state->areas + alloc_state->num_positional_areas, ix, sizeof(*alloc_state->areas),
|
||||
_comp_area);
|
||||
}
|
||||
|
||||
/* If there are gaps in our preferred areas, fill them from the sorted part of the array */
|
||||
if (preferred_count && preferred_count != ix_offset) {
|
||||
if (preferred_count && preferred_count != alloc_state->num_positional_areas) {
|
||||
for (s = 0; s < devices_needed; s++)
|
||||
if (!alloc_state->areas[s].pva) {
|
||||
alloc_state->areas[s].pva = alloc_state->areas[ix_offset].pva;
|
||||
alloc_state->areas[s].used = alloc_state->areas[ix_offset].used;
|
||||
alloc_state->areas[ix_offset++].pva = NULL;
|
||||
alloc_state->areas[s].pva = alloc_state->areas[alloc_state->num_positional_areas].pva;
|
||||
alloc_state->areas[s].used = alloc_state->areas[alloc_state->num_positional_areas].used;
|
||||
alloc_state->areas[alloc_state->num_positional_areas++].pva = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2752,14 +2780,14 @@ static int _find_some_parallel_space(struct alloc_handle *ah,
|
||||
/* FIXME This logic is due to its heritage and can be simplified! */
|
||||
if (alloc_state->log_area_count_still_needed) {
|
||||
/* How many areas are too small for the log? */
|
||||
while (too_small_for_log_count < ix_offset + ix &&
|
||||
(*(alloc_state->areas + ix_offset + ix - 1 -
|
||||
while (too_small_for_log_count < alloc_state->num_positional_areas + ix &&
|
||||
(*(alloc_state->areas + alloc_state->num_positional_areas + ix - 1 -
|
||||
too_small_for_log_count)).used < ah->log_len)
|
||||
too_small_for_log_count++;
|
||||
ix_log_offset = ix_offset + ix - too_small_for_log_count - ah->log_area_count;
|
||||
ix_log_offset = alloc_state->num_positional_areas + ix - too_small_for_log_count - ah->log_area_count;
|
||||
}
|
||||
|
||||
if (ix + ix_offset < devices_needed +
|
||||
if (ix + alloc_state->num_positional_areas < devices_needed +
|
||||
(alloc_state->log_area_count_still_needed ? alloc_state->log_area_count_still_needed +
|
||||
too_small_for_log_count : 0))
|
||||
return 1;
|
||||
@@ -2773,12 +2801,12 @@ static int _find_some_parallel_space(struct alloc_handle *ah,
|
||||
/*
|
||||
* This code covers the initial allocation - after that there is something to 'cling' to
|
||||
* and we shouldn't get this far.
|
||||
* ix_offset is assumed to be 0 with A_PARTITION_BY_TAGS.
|
||||
* alloc_state->num_positional_areas is assumed to be 0 with A_PARTITION_BY_TAGS.
|
||||
*
|
||||
* FIXME Consider a second attempt with A_PARTITION_BY_TAGS if, for example, the largest area
|
||||
* had all the tags set, but other areas don't.
|
||||
*/
|
||||
if ((alloc_parms->flags & A_PARTITION_BY_TAGS) && !ix_offset) {
|
||||
if ((alloc_parms->flags & A_PARTITION_BY_TAGS) && !alloc_state->num_positional_areas) {
|
||||
if (!_limit_to_one_area_per_tag(ah, alloc_state, ix_log_offset, &ix))
|
||||
return_0;
|
||||
|
||||
@@ -3235,7 +3263,7 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd,
|
||||
|
||||
ah->parallel_areas = parallel_areas;
|
||||
|
||||
if ((ah->cling_tag_list_cn = find_config_tree_node(cmd, allocation_cling_tag_list_CFG, NULL)))
|
||||
if ((ah->cling_tag_list_cn = find_config_tree_array(cmd, allocation_cling_tag_list_CFG, NULL)))
|
||||
(void) _validate_tag_list(ah->cling_tag_list_cn);
|
||||
|
||||
ah->maximise_cling = find_config_tree_bool(cmd, allocation_maximise_cling_CFG, NULL);
|
||||
@@ -4177,6 +4205,18 @@ int lv_rename_update(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The lvmlockd LV lock is only acquired here to ensure the LV is not
|
||||
* active on another host. This requests a transient LV lock.
|
||||
* If the LV is active, a persistent LV lock already exists in
|
||||
* lvmlockd, and the transient lock request does nothing.
|
||||
* If the LV is not active, then no LV lock exists and the transient
|
||||
* lock request acquires the LV lock (or fails). The transient lock
|
||||
* is automatically released when the command exits.
|
||||
*/
|
||||
if (!lockd_lv(cmd, lv, "ex", 0))
|
||||
return_0;
|
||||
|
||||
if (update_mda && !archive(vg))
|
||||
return_0;
|
||||
|
||||
@@ -4336,7 +4376,7 @@ static int _fsadm_cmd(struct cmd_context *cmd,
|
||||
argv[i++] = lv_path;
|
||||
|
||||
if (fcmd == FSADM_CMD_RESIZE) {
|
||||
if (dm_snprintf(size_buf, sizeof(size_buf), "%" PRIu64 "K",
|
||||
if (dm_snprintf(size_buf, sizeof(size_buf), FMTu64 "K",
|
||||
(uint64_t) lp->extents * (vg->extent_size / 2)) < 0) {
|
||||
log_error("Couldn't generate new LV size string");
|
||||
return 0;
|
||||
@@ -4569,7 +4609,9 @@ static int _lvresize_check_lv(struct cmd_context *cmd, struct logical_volume *lv
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lv_is_visible(lv) && !lv_is_thin_pool_metadata(lv)) {
|
||||
/* FIXME: use a status flag instead of the name "lvmlock". */
|
||||
|
||||
if (!lv_is_visible(lv) && !lv_is_thin_pool_metadata(lv) && strcmp(lv->name, "lvmlock")) {
|
||||
log_error("Can't resize internal logical volume %s", lv->name);
|
||||
return 0;
|
||||
}
|
||||
@@ -5065,16 +5107,6 @@ static int _lvresize_check_type(struct cmd_context *cmd, const struct logical_vo
|
||||
|
||||
if (lv_is_thin_volume(lv) && first_seg(lv)->external_lv &&
|
||||
(lp->resize == LV_EXTEND)) {
|
||||
/*
|
||||
* TODO: currently we do not support extension of already reduced thin volume.
|
||||
* But it might be possible to create combined mapping of some part of
|
||||
* the external origin followed by zero target.
|
||||
*/
|
||||
if (first_seg(lv)->external_lv->size > lv->size) {
|
||||
log_error("Extension of reduced thin volume with external origin is unsupported.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Validate thin target supports bigger size of thin volume then external origin */
|
||||
if (first_seg(lv)->external_lv->size <= lv->size &&
|
||||
!thin_pool_feature_supported(first_seg(lv)->pool_lv, THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND)) {
|
||||
@@ -5155,6 +5187,8 @@ static struct logical_volume *_lvresize_volume(struct cmd_context *cmd,
|
||||
lp->extents - lv->le_count,
|
||||
pvh, alloc, lp->approx_alloc))
|
||||
return_NULL;
|
||||
else if (!pool_check_overprovisioning(lv))
|
||||
return_NULL;
|
||||
|
||||
if (old_extents == lv->le_count)
|
||||
log_print_unless_silent("Size of logical volume %s unchanged from %s (%" PRIu32 " extents).",
|
||||
@@ -5229,6 +5263,13 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the LV is locked from activation, this lock call is a no-op.
|
||||
* Otherwise, this acquires a transient lock on the lv (not PERSISTENT).
|
||||
*/
|
||||
if (!lockd_lv(cmd, lv, "ex", 0))
|
||||
return_0;
|
||||
|
||||
if (lp->sizeargs &&
|
||||
!(lock_lv = _lvresize_volume(cmd, lv, lp, pvh)))
|
||||
return_0;
|
||||
@@ -5577,6 +5618,7 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
int format1_reload_required = 0;
|
||||
int visible;
|
||||
struct logical_volume *pool_lv = NULL;
|
||||
struct logical_volume *lock_lv = lv;
|
||||
struct lv_segment *cache_seg = NULL;
|
||||
int ask_discard;
|
||||
struct lv_list *lvl;
|
||||
@@ -5623,14 +5665,19 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
log_error("Can't remove logical volume %s used by a pool.",
|
||||
lv->name);
|
||||
return 0;
|
||||
} else if (lv_is_thin_volume(lv))
|
||||
} else if (lv_is_thin_volume(lv)) {
|
||||
pool_lv = first_seg(lv)->pool_lv;
|
||||
lock_lv = pool_lv;
|
||||
}
|
||||
|
||||
if (lv_is_locked(lv)) {
|
||||
log_error("Can't remove locked LV %s", lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lockd_lv(cmd, lock_lv, "ex", LDLV_PERSISTENT))
|
||||
return_0;
|
||||
|
||||
/* FIXME Ensure not referred to by another existing LVs */
|
||||
ask_discard = find_config_tree_bool(cmd, devices_issue_discards_CFG, NULL);
|
||||
|
||||
@@ -5805,6 +5852,9 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
|
||||
backup(vg);
|
||||
|
||||
lockd_lv(cmd, lock_lv, "un", LDLV_PERSISTENT);
|
||||
lockd_free_lv(cmd, vg, lv->name, &lv->lvid.id[1], lv->lock_args);
|
||||
|
||||
if (!suppress_remove_message && visible)
|
||||
log_print_unless_silent("Logical volume \"%s\" successfully removed", lv->name);
|
||||
|
||||
@@ -6131,7 +6181,7 @@ int remove_layers_for_segments(struct cmd_context *cmd,
|
||||
log_error("Layer boundary mismatch: "
|
||||
"%s:%" PRIu32 "-%" PRIu32 " on "
|
||||
"%s:%" PRIu32 " / "
|
||||
"%" PRIu32 "-%" PRIu32 " / ",
|
||||
FMTu32 "-" FMTu32 " / ",
|
||||
lv->name, seg->le, seg->area_len,
|
||||
layer_lv->name, seg_le(seg, s),
|
||||
lseg->le, lseg->area_len);
|
||||
@@ -6624,7 +6674,12 @@ int wipe_lv(struct logical_volume *lv, struct wipe_params wp)
|
||||
/* nothing to do */
|
||||
return 1;
|
||||
|
||||
sync_local_dev_names(lv->vg->cmd); /* Wait until devices are available */
|
||||
/* Wait until devices are available */
|
||||
if (!sync_local_dev_names(lv->vg->cmd)) {
|
||||
log_error("Failed to sync local devices before wiping LV %s.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lv_is_active_locally(lv)) {
|
||||
log_error("Volume \"%s/%s\" is not active locally.",
|
||||
@@ -6943,7 +6998,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
|
||||
if (seg_is_pool(lp))
|
||||
status |= LVM_WRITE; /* Pool is always writable */
|
||||
else if (seg_is_cache(lp) || seg_is_thin_volume(lp)) {
|
||||
else if (seg_is_cache(lp) || seg_is_thin_volume(lp)) {
|
||||
/* Resolve pool volume */
|
||||
if (!lp->pool_name) {
|
||||
/* Should be already checked */
|
||||
@@ -7019,7 +7074,11 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
display_lvname(origin_lv));
|
||||
return NULL;
|
||||
}
|
||||
} else if (pool_lv && seg_is_cache(lp)) {
|
||||
} else if (seg_is_cache(lp)) {
|
||||
if (!pool_lv) {
|
||||
log_error(INTERNAL_ERROR "Pool LV for cache is missing.");
|
||||
return NULL;
|
||||
}
|
||||
if (!lv_is_cache_pool(pool_lv)) {
|
||||
log_error("Logical volume %s is not a cache pool.",
|
||||
display_lvname(pool_lv));
|
||||
@@ -7163,6 +7222,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
if (!archive(vg))
|
||||
return_NULL;
|
||||
|
||||
|
||||
if (pool_lv && seg_is_thin_volume(lp)) {
|
||||
/* Ensure all stacked messages are submitted */
|
||||
if ((pool_is_active(pool_lv) || is_change_activating(lp->activate)) &&
|
||||
@@ -7187,6 +7247,14 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
lv->major, lv->minor);
|
||||
}
|
||||
|
||||
/*
|
||||
* The specific LV may not use a lock. lockd_init_lv() sets
|
||||
* lv->lock_args to NULL if this LV does not use its own lock.
|
||||
*/
|
||||
|
||||
if (!lockd_init_lv(vg->cmd, vg, lv, lp))
|
||||
return_NULL;
|
||||
|
||||
dm_list_splice(&lv->tags, &lp->tags);
|
||||
|
||||
if (!lv_extend(lv, create_segtype,
|
||||
@@ -7201,15 +7269,26 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
memlock_unlock(vg->cmd);
|
||||
|
||||
if (seg_is_cache_pool(lp) || seg_is_cache(lp)) {
|
||||
pool_lv = pool_lv ? : lv;
|
||||
first_seg(pool_lv)->chunk_size = lp->chunk_size;
|
||||
first_seg(pool_lv)->feature_flags = lp->feature_flags;
|
||||
/* TODO: some calc_policy solution for cache ? */
|
||||
if (!recalculate_pool_chunk_size_with_dev_hints(pool_lv, lp->passed_args,
|
||||
THIN_CHUNK_SIZE_CALC_METHOD_GENERIC)) {
|
||||
if (!cache_set_mode(first_seg(lv), lp->cache_mode)) {
|
||||
stack;
|
||||
goto revert_new_lv;
|
||||
}
|
||||
|
||||
if (!cache_set_policy(first_seg(lv), lp->policy_name, lp->policy_settings)) {
|
||||
stack;
|
||||
goto revert_new_lv;
|
||||
}
|
||||
|
||||
pool_lv = pool_lv ? : lv;
|
||||
if (lp->chunk_size) {
|
||||
first_seg(pool_lv)->chunk_size = lp->chunk_size;
|
||||
/* TODO: some calc_policy solution for cache ? */
|
||||
if (!recalculate_pool_chunk_size_with_dev_hints(pool_lv, lp->passed_args,
|
||||
THIN_CHUNK_SIZE_CALC_METHOD_GENERIC)) {
|
||||
stack;
|
||||
goto revert_new_lv;
|
||||
}
|
||||
}
|
||||
} else if (seg_is_raid(lp)) {
|
||||
first_seg(lv)->min_recovery_rate = lp->min_recovery_rate;
|
||||
first_seg(lv)->max_recovery_rate = lp->max_recovery_rate;
|
||||
@@ -7252,6 +7331,9 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
if (!pool_check_overprovisioning(lv))
|
||||
return_NULL;
|
||||
|
||||
/* FIXME Log allocation and attachment should have happened inside lv_extend. */
|
||||
if (lp->log_count &&
|
||||
!seg_is_raid(first_seg(lv)) && seg_is_mirrored(first_seg(lv))) {
|
||||
@@ -7326,11 +7408,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
stack;
|
||||
goto revert_new_lv;
|
||||
}
|
||||
/* When change is activating, don't duplicate backup call */
|
||||
if (!is_change_activating(lp->activate))
|
||||
backup(vg);
|
||||
}
|
||||
if (is_change_activating(lp->activate)) {
|
||||
if (!dm_list_empty(&first_seg(pool_lv)->thin_messages)) {
|
||||
/* Send message so that table preload knows new thin */
|
||||
if (!lv_is_active(pool_lv)) {
|
||||
/* Avoid multiple thin-pool activations in this case */
|
||||
@@ -7348,25 +7427,24 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
}
|
||||
}
|
||||
/* Keep thin pool active until thin volume is activated */
|
||||
if (!update_pool_lv(pool_lv, (thin_pool_was_active < 0) ? 1 : 0)) {
|
||||
if (!update_pool_lv(pool_lv, 1)) {
|
||||
stack;
|
||||
goto revert_new_lv;
|
||||
}
|
||||
}
|
||||
backup(vg);
|
||||
|
||||
backup(vg);
|
||||
if (!lv_active_change(cmd, lv, lp->activate, 0)) {
|
||||
log_error("Failed to activate thin %s.", lv->name);
|
||||
goto deactivate_and_revert_new_lv;
|
||||
}
|
||||
|
||||
if (!lv_active_change(cmd, lv, lp->activate, 0)) {
|
||||
log_error("Failed to activate thin %s.", lv->name);
|
||||
goto deactivate_and_revert_new_lv;
|
||||
}
|
||||
|
||||
/* Restore inactive state if needed */
|
||||
if (!thin_pool_was_active &&
|
||||
!deactivate_lv(cmd, pool_lv)) {
|
||||
log_error("Failed to deactivate thin pool %s.",
|
||||
display_lvname(pool_lv));
|
||||
return NULL;
|
||||
}
|
||||
/* Restore inactive state if needed */
|
||||
if (!thin_pool_was_active &&
|
||||
!deactivate_lv(cmd, pool_lv)) {
|
||||
log_error("Failed to deactivate thin pool %s.",
|
||||
display_lvname(pool_lv));
|
||||
return NULL;
|
||||
}
|
||||
} else if (lp->snapshot) {
|
||||
lv->status |= LV_TEMPORARY;
|
||||
@@ -7395,13 +7473,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
}
|
||||
}
|
||||
|
||||
if (lv_is_cache_pool(lv) && !origin_lv) {
|
||||
if (lp->cache_policy && !lv_cache_setpolicy(lv, lp->cache_policy))
|
||||
return NULL; /* revert? */
|
||||
if (!lv_update_and_reload(lv))
|
||||
return NULL; /* FIXME: revert */
|
||||
}
|
||||
|
||||
if (seg_is_cache(lp) || (origin_lv && lv_is_cache_pool(lv))) {
|
||||
/* Finish cache conversion magic */
|
||||
if (origin_lv) {
|
||||
@@ -7421,8 +7492,11 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
}
|
||||
lv = tmp_lv;
|
||||
|
||||
if (lp->cache_policy && !lv_cache_setpolicy(lv, lp->cache_policy))
|
||||
return NULL; /* revert? */
|
||||
if (!cache_set_mode(first_seg(lv), lp->cache_mode))
|
||||
return_NULL; /* revert? */
|
||||
|
||||
if (!cache_set_policy(first_seg(lv), lp->policy_name, lp->policy_settings))
|
||||
return_NULL; /* revert? */
|
||||
|
||||
if (!lv_update_and_reload(lv)) {
|
||||
/* FIXME Do a better revert */
|
||||
@@ -7437,6 +7511,13 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
goto deactivate_and_revert_new_lv; /* Let's retry on error path */
|
||||
}
|
||||
|
||||
/* Get in sync with deactivation, before reusing LV as snapshot */
|
||||
if (!sync_local_dev_names(lv->vg->cmd)) {
|
||||
log_error("Failed to sync local devices before creating snapshot using %s.",
|
||||
display_lvname(lv));
|
||||
goto revert_new_lv;
|
||||
}
|
||||
|
||||
/* Create zero origin volume for spare snapshot */
|
||||
if (lp->virtual_extents &&
|
||||
!(origin_lv = _create_virtual_origin(cmd, vg, lv->name,
|
||||
@@ -7494,6 +7575,8 @@ deactivate_and_revert_new_lv:
|
||||
}
|
||||
|
||||
revert_new_lv:
|
||||
lockd_free_lv(vg->cmd, vg, lp->lv_name, &lv->lvid.id[1], lp->lock_args);
|
||||
|
||||
/* FIXME Better to revert to backup of metadata? */
|
||||
if (!lv_remove(lv) || !vg_write(vg) || !vg_commit(vg))
|
||||
log_error("Manual intervention may be required to remove "
|
||||
|
||||
@@ -208,6 +208,26 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
|
||||
}
|
||||
|
||||
}
|
||||
if (seg_is_cache_pool(seg) &&
|
||||
!dm_list_empty(&seg->lv->segs_using_this_lv)) {
|
||||
switch (seg->feature_flags &
|
||||
(DM_CACHE_FEATURE_PASSTHROUGH |
|
||||
DM_CACHE_FEATURE_WRITETHROUGH |
|
||||
DM_CACHE_FEATURE_WRITEBACK)) {
|
||||
case DM_CACHE_FEATURE_PASSTHROUGH:
|
||||
case DM_CACHE_FEATURE_WRITETHROUGH:
|
||||
case DM_CACHE_FEATURE_WRITEBACK:
|
||||
break;
|
||||
default:
|
||||
log_error("LV %s has invalid cache's feature flag.",
|
||||
lv->name);
|
||||
inc_error_count;
|
||||
}
|
||||
if (!seg->policy_name) {
|
||||
log_error("LV %s is missing cache policy name.", lv->name);
|
||||
inc_error_count;
|
||||
}
|
||||
}
|
||||
if (seg_is_pool(seg)) {
|
||||
if (seg->area_count != 1 ||
|
||||
seg_type(seg, 0) != AREA_LV) {
|
||||
@@ -231,8 +251,7 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
|
||||
inc_error_count;
|
||||
}
|
||||
|
||||
if (seg_is_pool(seg) &&
|
||||
!validate_pool_chunk_size(lv->vg->cmd, seg->segtype, seg->chunk_size)) {
|
||||
if (!validate_pool_chunk_size(lv->vg->cmd, seg->segtype, seg->chunk_size)) {
|
||||
log_error("LV %s: %s segment %u has invalid chunk size %u.",
|
||||
lv->name, seg->segtype->name, seg_count, seg->chunk_size);
|
||||
inc_error_count;
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
#define THIN_POOL_DATA UINT64_C(0x0000004000000000) /* LV - Internal use only */
|
||||
#define THIN_POOL_METADATA UINT64_C(0x0000008000000000) /* LV - Internal use only */
|
||||
#define POOL_METADATA_SPARE UINT64_C(0x0000010000000000) /* LV - Internal use only */
|
||||
|
||||
#define LV_WRITEMOSTLY UINT64_C(0x0000020000000000) /* LV (RAID1) */
|
||||
|
||||
#define LV_ACTIVATION_SKIP UINT64_C(0x0000040000000000) /* LV */
|
||||
@@ -132,7 +131,8 @@
|
||||
#define PV_ALLOCATION_PROHIBITED UINT64_C(0x0010000000000000) /* PV - internal use only - allocation prohibited
|
||||
e.g. to prohibit allocation of a RAID image
|
||||
on a PV already holing an image of the RAID set */
|
||||
/* Next unused flag: UINT64_C(0x0080000000000000) */
|
||||
#define LOCKD_SANLOCK_LV UINT64_C(0x0080000000000000) /* LV - Internal use only */
|
||||
/* Next unused flag: UINT64_C(0x0100000000000000) */
|
||||
|
||||
/* Format features flags */
|
||||
#define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */
|
||||
@@ -181,6 +181,7 @@
|
||||
#define FAILED_RECOVERY 0x00000200U
|
||||
#define FAILED_SYSTEMID 0x00000400U
|
||||
#define FAILED_LOCK_TYPE 0x00000800U
|
||||
#define FAILED_LOCK_MODE 0x00001000U
|
||||
#define SUCCESS 0x00000000U
|
||||
|
||||
#define VGMETADATACOPIES_ALL UINT32_MAX
|
||||
@@ -228,6 +229,7 @@
|
||||
#define lv_is_pool_data(lv) (((lv)->status & (CACHE_POOL_DATA | THIN_POOL_DATA)) ? 1 : 0)
|
||||
#define lv_is_pool_metadata(lv) (((lv)->status & (CACHE_POOL_METADATA | THIN_POOL_METADATA)) ? 1 : 0)
|
||||
#define lv_is_pool_metadata_spare(lv) (((lv)->status & POOL_METADATA_SPARE) ? 1 : 0)
|
||||
#define lv_is_lockd_sanlock_lv(lv) (((lv)->status & LOCKD_SANLOCK_LV) ? 1 : 0)
|
||||
|
||||
#define lv_is_rlog(lv) (((lv)->status & REPLICATOR_LOG) ? 1 : 0)
|
||||
|
||||
@@ -262,6 +264,14 @@ typedef enum {
|
||||
THIN_DISCARDS_PASSDOWN,
|
||||
} thin_discards_t;
|
||||
|
||||
typedef enum {
|
||||
LOCK_TYPE_INVALID = -1,
|
||||
LOCK_TYPE_NONE = 0,
|
||||
LOCK_TYPE_CLVM = 1,
|
||||
LOCK_TYPE_DLM = 2,
|
||||
LOCK_TYPE_SANLOCK = 3,
|
||||
} lock_type_t;
|
||||
|
||||
struct cmd_context;
|
||||
struct format_handler;
|
||||
struct labeller;
|
||||
@@ -494,6 +504,12 @@ struct vg_list {
|
||||
struct volume_group *vg;
|
||||
};
|
||||
|
||||
struct vgnameid_list {
|
||||
struct dm_list list;
|
||||
const char *vg_name;
|
||||
const char *vgid;
|
||||
};
|
||||
|
||||
#define PV_PE_START_CALC ((uint64_t) -1) /* Calculate pe_start value */
|
||||
|
||||
struct pvcreate_restorable_params {
|
||||
@@ -606,6 +622,8 @@ void lv_set_hidden(struct logical_volume *lv);
|
||||
|
||||
struct dm_list *get_vgnames(struct cmd_context *cmd, int include_internal);
|
||||
struct dm_list *get_vgids(struct cmd_context *cmd, int include_internal);
|
||||
int get_vgnameids(struct cmd_context *cmd, struct dm_list *vgnameids,
|
||||
const char *only_this_vgname, int include_internal);
|
||||
int scan_vgs_for_pvs(struct cmd_context *cmd, uint32_t warn_flags);
|
||||
|
||||
int pv_write(struct cmd_context *cmd, struct physical_volume *pv, int allow_non_orphan);
|
||||
@@ -632,9 +650,9 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
* Return a handle to VG metadata.
|
||||
*/
|
||||
struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
|
||||
const char *vgid, uint32_t flags);
|
||||
const char *vgid, uint32_t flags, uint32_t lockd_state);
|
||||
struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_name,
|
||||
const char *vgid, uint32_t flags);
|
||||
const char *vgid, uint32_t flags, uint32_t lockd_state);
|
||||
|
||||
/*
|
||||
* Test validity of a VG handle.
|
||||
@@ -677,6 +695,7 @@ struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name);
|
||||
int vg_remove_mdas(struct volume_group *vg);
|
||||
int vg_remove_check(struct volume_group *vg);
|
||||
void vg_remove_pvs(struct volume_group *vg);
|
||||
int vg_remove_direct(struct volume_group *vg);
|
||||
int vg_remove(struct volume_group *vg);
|
||||
int vg_rename(struct cmd_context *cmd, struct volume_group *vg,
|
||||
const char *new_name);
|
||||
@@ -730,6 +749,8 @@ int lv_empty(struct logical_volume *lv);
|
||||
/* Empty an LV and add error segment */
|
||||
int replace_lv_with_error_segment(struct logical_volume *lv);
|
||||
|
||||
int lv_refresh_suspend_resume(struct cmd_context *cmd, struct logical_volume *lv);
|
||||
|
||||
/* Entry point for all LV extent allocations */
|
||||
int lv_extend(struct logical_volume *lv,
|
||||
const struct segment_type *segtype,
|
||||
@@ -820,7 +841,8 @@ typedef enum activation_change {
|
||||
CHANGE_AEY = 2, /* activate exclusively */
|
||||
CHANGE_ALY = 3, /* activate locally */
|
||||
CHANGE_ALN = 4, /* deactivate locally */
|
||||
CHANGE_AAY = 5 /* automatic activation */
|
||||
CHANGE_AAY = 5, /* automatic activation */
|
||||
CHANGE_ASY = 6 /* activate shared */
|
||||
} activation_change_t;
|
||||
|
||||
/* Returns true, when change activates device */
|
||||
@@ -852,12 +874,15 @@ struct lvcreate_params {
|
||||
#define THIN_CHUNK_SIZE_CALC_METHOD_GENERIC 0x01
|
||||
#define THIN_CHUNK_SIZE_CALC_METHOD_PERFORMANCE 0x02
|
||||
int thin_chunk_size_calc_policy;
|
||||
unsigned needs_lockd_init : 1;
|
||||
|
||||
const char *vg_name; /* only-used when VG is not yet opened (in /tools) */
|
||||
const char *lv_name; /* all */
|
||||
const char *origin_name; /* snap */
|
||||
const char *pool_name; /* thin */
|
||||
|
||||
const char *lock_args;
|
||||
|
||||
/* Keep args given by the user on command line */
|
||||
/* FIXME: create some more universal solution here */
|
||||
#define PASS_ARG_CHUNK_SIZE 0x01
|
||||
@@ -876,8 +901,9 @@ struct lvcreate_params {
|
||||
uint32_t min_recovery_rate; /* RAID */
|
||||
uint32_t max_recovery_rate; /* RAID */
|
||||
|
||||
uint64_t feature_flags; /* cache */
|
||||
struct dm_config_tree *cache_policy; /* cache */
|
||||
const char *cache_mode; /* cache */
|
||||
const char *policy_name; /* cache */
|
||||
struct dm_config_tree *policy_settings; /* cache */
|
||||
|
||||
const struct segment_type *segtype; /* all */
|
||||
unsigned target_attr; /* all */
|
||||
@@ -1127,8 +1153,11 @@ struct lv_status_cache {
|
||||
dm_percent_t dirty_usage;
|
||||
};
|
||||
|
||||
const char *get_cache_pool_cachemode_name(const struct lv_segment *seg);
|
||||
int set_cache_pool_feature(uint64_t *feature_flags, const char *str);
|
||||
const char *get_cache_mode_name(const struct lv_segment *cache_seg);
|
||||
int cache_mode_is_set(const struct lv_segment *seg);
|
||||
int cache_set_mode(struct lv_segment *cache_seg, const char *str);
|
||||
int cache_set_policy(struct lv_segment *cache_seg, const char *name,
|
||||
const struct dm_config_tree *settings);
|
||||
int update_cache_pool_params(const struct segment_type *segtype,
|
||||
struct volume_group *vg, unsigned attr,
|
||||
int passed_args, uint32_t pool_data_extents,
|
||||
@@ -1139,7 +1168,6 @@ int validate_lv_cache_create_origin(const struct logical_volume *origin_lv);
|
||||
struct logical_volume *lv_cache_create(struct logical_volume *pool,
|
||||
struct logical_volume *origin);
|
||||
int lv_cache_remove(struct logical_volume *cache_lv);
|
||||
int lv_cache_setpolicy(struct logical_volume *cache_lv, struct dm_config_tree *pol);
|
||||
int wipe_cache_pool(struct logical_volume *cache_pool_lv);
|
||||
/* -- metadata/cache_manip.c */
|
||||
|
||||
@@ -1200,6 +1228,8 @@ struct vgcreate_params {
|
||||
int clustered; /* FIXME: put this into a 'status' variable instead? */
|
||||
uint32_t vgmetadatacopies;
|
||||
const char *system_id;
|
||||
const char *lock_type;
|
||||
const char *lock_args;
|
||||
};
|
||||
|
||||
int validate_major_minor(const struct cmd_context *cmd,
|
||||
@@ -1211,4 +1241,7 @@ int vgcreate_params_validate(struct cmd_context *cmd,
|
||||
int validate_vg_rename_params(struct cmd_context *cmd,
|
||||
const char *vg_name_old,
|
||||
const char *vg_name_new);
|
||||
|
||||
int is_lockd_type(const char *lock_type);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "locking.h"
|
||||
#include "archiver.h"
|
||||
#include "defaults.h"
|
||||
#include "lvmlockd.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <sys/param.h>
|
||||
@@ -557,20 +558,14 @@ void vg_remove_pvs(struct volume_group *vg)
|
||||
}
|
||||
}
|
||||
|
||||
int vg_remove(struct volume_group *vg)
|
||||
int vg_remove_direct(struct volume_group *vg)
|
||||
{
|
||||
struct physical_volume *pv;
|
||||
struct pv_list *pvl;
|
||||
int ret = 1;
|
||||
|
||||
if (!lock_vol(vg->cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) {
|
||||
log_error("Can't get lock for orphan PVs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!vg_remove_mdas(vg)) {
|
||||
log_error("vg_remove_mdas %s failed", vg->name);
|
||||
unlock_vg(vg->cmd, VG_ORPHANS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -604,6 +599,8 @@ int vg_remove(struct volume_group *vg)
|
||||
if (!lvmetad_vg_remove(vg))
|
||||
stack;
|
||||
|
||||
lockd_vg_update(vg);
|
||||
|
||||
if (!backup_remove(vg->cmd, vg->name))
|
||||
stack;
|
||||
|
||||
@@ -612,6 +609,20 @@ int vg_remove(struct volume_group *vg)
|
||||
else
|
||||
log_error("Volume group \"%s\" not properly removed", vg->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vg_remove(struct volume_group *vg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!lock_vol(vg->cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) {
|
||||
log_error("Can't get lock for orphan PVs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = vg_remove_direct(vg);
|
||||
|
||||
unlock_vg(vg->cmd, VG_ORPHANS);
|
||||
return ret;
|
||||
}
|
||||
@@ -1504,7 +1515,7 @@ static int _pvcreate_check(struct cmd_context *cmd, const char *name,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (wiped)
|
||||
if (*wiped)
|
||||
filter_refresh_needed = scan_needed = 1;
|
||||
|
||||
if (sigint_caught())
|
||||
@@ -2428,6 +2439,7 @@ struct validate_hash {
|
||||
struct dm_hash_table *lvname;
|
||||
struct dm_hash_table *lvid;
|
||||
struct dm_hash_table *pvid;
|
||||
struct dm_hash_table *lv_lock_args;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2472,6 +2484,75 @@ static int _lv_validate_references_single(struct logical_volume *lv, void *data)
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Format is <version>:<info>
|
||||
*/
|
||||
static int _validate_lock_args_chars(const char *lock_args)
|
||||
{
|
||||
int i;
|
||||
char c;
|
||||
int found_colon = 0;
|
||||
int r = 1;
|
||||
|
||||
for (i = 0; i < strlen(lock_args); i++) {
|
||||
c = lock_args[i];
|
||||
|
||||
if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+' && c != ':') {
|
||||
log_error(INTERNAL_ERROR "Invalid character at index %d of lock_args \"%s\"",
|
||||
i, lock_args);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
if (c == ':' && found_colon) {
|
||||
log_error(INTERNAL_ERROR "Invalid colon at index %d of lock_args \"%s\"",
|
||||
i, lock_args);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
if (c == ':')
|
||||
found_colon = 1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _validate_vg_lock_args(struct volume_group *vg)
|
||||
{
|
||||
if (!_validate_lock_args_chars(vg->lock_args)) {
|
||||
log_error(INTERNAL_ERROR "VG %s has invalid lock_args chars", vg->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* For lock_type sanlock, LV lock_args are <version>:<info>
|
||||
* For lock_type dlm, LV lock_args are not used, and lock_args is
|
||||
* just set to "dlm".
|
||||
*/
|
||||
static int _validate_lv_lock_args(struct logical_volume *lv)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
if (!strcmp(lv->vg->lock_type, "sanlock")) {
|
||||
if (!_validate_lock_args_chars(lv->lock_args)) {
|
||||
log_error(INTERNAL_ERROR "LV %s/%s has invalid lock_args chars",
|
||||
lv->vg->name, display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else if (!strcmp(lv->vg->lock_type, "dlm")) {
|
||||
if (strcmp(lv->lock_args, "dlm")) {
|
||||
log_error(INTERNAL_ERROR "LV %s/%s has invalid lock_args \"%s\"",
|
||||
lv->vg->name, display_lvname(lv), lv->lock_args);
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int vg_validate(struct volume_group *vg)
|
||||
{
|
||||
struct pv_list *pvl;
|
||||
@@ -2786,6 +2867,129 @@ int vg_validate(struct volume_group *vg)
|
||||
|
||||
if (vg_max_lv_reached(vg))
|
||||
stack;
|
||||
|
||||
if (!(vhash.lv_lock_args = dm_hash_create(lv_count))) {
|
||||
log_error("Failed to allocate lv_lock_args hash");
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_lockd_type(vg->lock_type)) {
|
||||
if (!vg->lock_args) {
|
||||
log_error(INTERNAL_ERROR "VG %s with lock_type %s without lock_args",
|
||||
vg->name, vg->lock_type);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
if (vg_is_clustered(vg)) {
|
||||
log_error(INTERNAL_ERROR "VG %s with lock_type %s is clustered",
|
||||
vg->name, vg->lock_type);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
if (vg->system_id && vg->system_id[0]) {
|
||||
log_error(INTERNAL_ERROR "VG %s with lock_type %s has system_id %s",
|
||||
vg->name, vg->lock_type, vg->system_id);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
if (strcmp(vg->lock_type, "sanlock") && strcmp(vg->lock_type, "dlm")) {
|
||||
log_error(INTERNAL_ERROR "VG %s has unknown lock_type %s",
|
||||
vg->name, vg->lock_type);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
if (!_validate_vg_lock_args(vg))
|
||||
r = 0;
|
||||
} else {
|
||||
if (vg->lock_args) {
|
||||
log_error(INTERNAL_ERROR "VG %s has lock_args %s without lock_type",
|
||||
vg->name, vg->lock_args);
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dm_list_iterate_items(lvl, &vg->lvs) {
|
||||
if (is_lockd_type(vg->lock_type)) {
|
||||
if (lockd_lv_uses_lock(lvl->lv)) {
|
||||
if (vg->skip_validate_lock_args)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* FIXME: make missing lock_args an error.
|
||||
* There are at least two cases where this
|
||||
* check doesn't work correctly:
|
||||
*
|
||||
* 1. When creating a cow snapshot,
|
||||
* (lvcreate -s -L1M -n snap1 vg/lv1),
|
||||
* lockd_lv_uses_lock() uses lv_is_cow()
|
||||
* which depends on lv->snapshot being
|
||||
* set, but it's not set at this point,
|
||||
* so lockd_lv_uses_lock() cannot identify
|
||||
* the LV as a cow_lv, and thinks it needs
|
||||
* a lock when it doesn't. To fix this we
|
||||
* probably need to validate by finding the
|
||||
* origin LV, then finding all its snapshots
|
||||
* which will have no lock_args.
|
||||
*
|
||||
* 2. When converting an LV to a thin pool
|
||||
* without using an existing metadata LV,
|
||||
* (lvconvert --type thin-pool vg/poolX),
|
||||
* there is an intermediate LV created,
|
||||
* probably for the metadata LV, and
|
||||
* validate is called on the VG in this
|
||||
* intermediate state, which finds the
|
||||
* newly created LV which is not yet
|
||||
* identified as a metadata LV, and
|
||||
* does not have any lock_args. To fix
|
||||
* this we might be able to find the place
|
||||
* where the intermediate LV is created,
|
||||
* and set new variable on it like for vgs,
|
||||
* lv->skip_validate_lock_args.
|
||||
*/
|
||||
if (!lvl->lv->lock_args) {
|
||||
/*
|
||||
log_verbose("LV %s/%s missing lock_args",
|
||||
vg->name, lvl->lv->name);
|
||||
r = 0;
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_validate_lv_lock_args(lvl->lv)) {
|
||||
r = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(vg->lock_type, "sanlock")) {
|
||||
if (dm_hash_lookup(vhash.lv_lock_args, lvl->lv->lock_args)) {
|
||||
log_error(INTERNAL_ERROR "LV %s/%s has duplicate lock_args %s.",
|
||||
vg->name, lvl->lv->name, lvl->lv->lock_args);
|
||||
r = 0;
|
||||
}
|
||||
|
||||
if (!dm_hash_insert(vhash.lv_lock_args, lvl->lv->lock_args, lvl)) {
|
||||
log_error("Failed to hash lvname.");
|
||||
r = 0;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if (lvl->lv->lock_args) {
|
||||
log_error(INTERNAL_ERROR "LV %s/%s shouldn't have lock_args",
|
||||
vg->name, lvl->lv->name);
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (lvl->lv->lock_args) {
|
||||
log_error(INTERNAL_ERROR "LV %s/%s with no lock_type has lock_args %s",
|
||||
vg->name, lvl->lv->name, lvl->lv->lock_args);
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (vhash.lvid)
|
||||
dm_hash_destroy(vhash.lvid);
|
||||
@@ -2793,6 +2997,8 @@ out:
|
||||
dm_hash_destroy(vhash.lvname);
|
||||
if (vhash.pvid)
|
||||
dm_hash_destroy(vhash.pvid);
|
||||
if (vhash.lv_lock_args)
|
||||
dm_hash_destroy(vhash.lv_lock_args);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -2806,8 +3012,19 @@ int vg_write(struct volume_group *vg)
|
||||
struct dm_list *mdah;
|
||||
struct pv_to_create *pv_to_create;
|
||||
struct metadata_area *mda;
|
||||
struct lv_list *lvl;
|
||||
int revert = 0, wrote = 0;
|
||||
|
||||
dm_list_iterate_items(lvl, &vg->lvs) {
|
||||
if (lvl->lv->lock_args && !strcmp(lvl->lv->lock_args, "pending")) {
|
||||
if (!lockd_init_lv_args(vg->cmd, vg, lvl->lv, vg->lock_type, &lvl->lv->lock_args)) {
|
||||
log_error("Cannot allocate lock for new LV.");
|
||||
return 0;
|
||||
}
|
||||
lvl->lv->new_lock_args = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vg_validate(vg))
|
||||
return_0;
|
||||
|
||||
@@ -2974,6 +3191,8 @@ int vg_commit(struct volume_group *vg)
|
||||
|
||||
cache_updated = _vg_commit_mdas(vg);
|
||||
|
||||
lockd_vg_update(vg);
|
||||
|
||||
if (cache_updated) {
|
||||
/* Instruct remote nodes to upgrade cached metadata. */
|
||||
if (!remote_commit_cached_metadata(vg))
|
||||
@@ -3007,6 +3226,14 @@ int vg_commit(struct volume_group *vg)
|
||||
void vg_revert(struct volume_group *vg)
|
||||
{
|
||||
struct metadata_area *mda;
|
||||
struct lv_list *lvl;
|
||||
|
||||
dm_list_iterate_items(lvl, &vg->lvs) {
|
||||
if (lvl->lv->new_lock_args) {
|
||||
lockd_free_lv(vg->cmd, vg, lvl->lv->name, &lvl->lv->lvid.id[1], lvl->lv->lock_args);
|
||||
lvl->lv->new_lock_args = 0;
|
||||
}
|
||||
}
|
||||
|
||||
release_vg(vg->vg_precommitted); /* VG is no longer needed */
|
||||
vg->vg_precommitted = NULL;
|
||||
@@ -3224,6 +3451,33 @@ static int _check_mda_in_use(struct metadata_area *mda, void *_in_use)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _wipe_outdated_pvs(struct cmd_context *cmd, struct volume_group *vg, struct dm_list *to_check)
|
||||
{
|
||||
struct pv_list *pvl, *pvl2;
|
||||
char uuid[64] __attribute__((aligned(8)));
|
||||
dm_list_iterate_items(pvl, to_check) {
|
||||
dm_list_iterate_items(pvl2, &vg->pvs) {
|
||||
if (pvl->pv->dev == pvl2->pv->dev)
|
||||
goto next_pv;
|
||||
}
|
||||
if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid)))
|
||||
return_0;
|
||||
log_warn("WARNING: Removing PV %s (%s) that no longer belongs to VG %s",
|
||||
pv_dev_name(pvl->pv), uuid, vg->name);
|
||||
if (!pv_write_orphan(cmd, pvl->pv))
|
||||
return_0;
|
||||
|
||||
/* Refresh metadata after orphan write */
|
||||
if (!drop_cached_metadata(vg)) {
|
||||
log_error("Unable to drop cached metadata for VG %s while wiping outdated PVs.", vg->name);
|
||||
return 0;
|
||||
}
|
||||
next_pv:
|
||||
;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Caller sets consistent to 1 if it's safe for vg_read_internal to correct
|
||||
* inconsistent metadata on disk (i.e. the VG write lock is held).
|
||||
* This guarantees only consistent metadata is returned.
|
||||
@@ -3257,9 +3511,8 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
int inconsistent_mda_count = 0;
|
||||
unsigned use_precommitted = precommitted;
|
||||
struct dm_list *pvids;
|
||||
struct pv_list *pvl, *pvl2;
|
||||
struct pv_list *pvl;
|
||||
struct dm_list all_pvs;
|
||||
char uuid[64] __attribute__((aligned(8)));
|
||||
unsigned seqno = 0;
|
||||
int reappeared = 0;
|
||||
struct cached_vg_fmtdata *vg_fmtdata = NULL; /* Additional format-specific data about the vg */
|
||||
@@ -3284,6 +3537,11 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
*consistent = _repair_inconsistent_vg(correct_vg);
|
||||
else
|
||||
*consistent = !reappeared;
|
||||
if (_wipe_outdated_pvs(cmd, correct_vg, &correct_vg->pvs_outdated)) {
|
||||
/* clear the list */
|
||||
dm_list_init(&correct_vg->pvs_outdated);
|
||||
lvmetad_vg_clear_outdated_pvs(correct_vg);
|
||||
}
|
||||
}
|
||||
return correct_vg;
|
||||
}
|
||||
@@ -3684,28 +3942,10 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dm_list_iterate_items(pvl, &all_pvs) {
|
||||
dm_list_iterate_items(pvl2, &correct_vg->pvs) {
|
||||
if (pvl->pv->dev == pvl2->pv->dev)
|
||||
goto next_pv;
|
||||
}
|
||||
if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid))) {
|
||||
_free_pv_list(&all_pvs);
|
||||
release_vg(correct_vg);
|
||||
return_NULL;
|
||||
}
|
||||
log_warn("WARNING: Removing PV %s (%s) that no longer belongs to VG %s",
|
||||
pv_dev_name(pvl->pv), uuid, correct_vg->name);
|
||||
if (!pv_write_orphan(cmd, pvl->pv)) {
|
||||
_free_pv_list(&all_pvs);
|
||||
release_vg(correct_vg);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
/* Refresh metadata after orphan write */
|
||||
drop_cached_metadata(correct_vg);
|
||||
next_pv:
|
||||
;
|
||||
if (!_wipe_outdated_pvs(cmd, correct_vg, &all_pvs)) {
|
||||
_free_pv_list(&all_pvs);
|
||||
release_vg(correct_vg);
|
||||
return_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3808,6 +4048,16 @@ static struct volume_group *_vg_read_by_vgid(struct cmd_context *cmd,
|
||||
release_vg(vg);
|
||||
}
|
||||
|
||||
/*
|
||||
* When using lvmlockd we should never reach this point.
|
||||
* The VG is locked, then vg_read() is done, which gets
|
||||
* the latest VG from lvmetad, or disk if lvmetad has
|
||||
* been invalidated. When we get here the VG should
|
||||
* always be cached and returned above.
|
||||
*/
|
||||
if (lvmlockd_use())
|
||||
log_error(INTERNAL_ERROR "vg_read_by_vgid failed with lvmlockd");
|
||||
|
||||
/* Mustn't scan if memory locked: ensure cache gets pre-populated! */
|
||||
if (critical_section())
|
||||
return_NULL;
|
||||
@@ -4017,6 +4267,54 @@ struct dm_list *get_vgids(struct cmd_context *cmd, int include_internal)
|
||||
return lvmcache_get_vgids(cmd, include_internal);
|
||||
}
|
||||
|
||||
int get_vgnameids(struct cmd_context *cmd, struct dm_list *vgnameids,
|
||||
const char *only_this_vgname, int include_internal)
|
||||
{
|
||||
struct vgnameid_list *vgnl;
|
||||
struct format_type *fmt;
|
||||
|
||||
if (only_this_vgname) {
|
||||
if (!(vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl)))) {
|
||||
log_error("vgnameid_list allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vgnl->vg_name = dm_pool_strdup(cmd->mem, only_this_vgname);
|
||||
vgnl->vgid = NULL;
|
||||
dm_list_add(vgnameids, &vgnl->list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lvmetad_active()) {
|
||||
/*
|
||||
* This just gets the list of names/ids from lvmetad
|
||||
* and does not populate lvmcache.
|
||||
*/
|
||||
lvmetad_get_vgnameids(cmd, vgnameids);
|
||||
|
||||
if (include_internal) {
|
||||
dm_list_iterate_items(fmt, &cmd->formats) {
|
||||
if (!(vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl)))) {
|
||||
log_error("vgnameid_list allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
vgnl->vg_name = dm_pool_strdup(cmd->mem, fmt->orphan_vg_name);
|
||||
vgnl->vgid = NULL;
|
||||
dm_list_add(vgnameids, &vgnl->list);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The non-lvmetad case. This function begins by calling
|
||||
* lvmcache_label_scan() to populate lvmcache.
|
||||
*/
|
||||
lvmcache_get_vgnameids(cmd, include_internal, vgnameids);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _get_pvs(struct cmd_context *cmd, uint32_t warn_flags,
|
||||
struct dm_list *pvslist, struct dm_list *vgslist)
|
||||
{
|
||||
@@ -4409,13 +4707,13 @@ static struct volume_group *_recover_vg(struct cmd_context *cmd,
|
||||
return (struct volume_group *)vg;
|
||||
}
|
||||
|
||||
static int _allow_system_id(struct cmd_context *cmd, const char *system_id)
|
||||
static int _allow_extra_system_id(struct cmd_context *cmd, const char *system_id)
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
const struct dm_config_value *cv;
|
||||
const char *str;
|
||||
|
||||
if (!(cn = find_config_tree_node(cmd, local_extra_system_ids_CFG, NULL)))
|
||||
if (!(cn = find_config_tree_array(cmd, local_extra_system_ids_CFG, NULL)))
|
||||
return 0;
|
||||
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
@@ -4448,20 +4746,73 @@ static int _access_vg_clustered(struct cmd_context *cmd, struct volume_group *vg
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _access_vg_lock_type(struct cmd_context *cmd, struct volume_group *vg)
|
||||
static int _access_vg_lock_type(struct cmd_context *cmd, struct volume_group *vg,
|
||||
uint32_t lockd_state, uint32_t *failure)
|
||||
{
|
||||
if (!is_real_vg(vg->name))
|
||||
return 1;
|
||||
|
||||
if (cmd->lockd_vg_disable)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Until lock_type support is added, reject any VG that has a lock_type.
|
||||
* Local VG requires no lock from lvmlockd.
|
||||
*/
|
||||
if (vg->lock_type && vg->lock_type[0] && strcmp(vg->lock_type, "none")) {
|
||||
log_error("Cannot access VG %s with unsupported lock_type %s.",
|
||||
vg->name, vg->lock_type);
|
||||
if (!is_lockd_type(vg->lock_type))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* When lvmlockd is not used, lockd VGs are ignored by lvm
|
||||
* and cannot be used, with two exceptions:
|
||||
*
|
||||
* . The --shared option allows them to be revealed with
|
||||
* reporting/display commands.
|
||||
*
|
||||
* . If a command asks to operate on one specifically
|
||||
* by name, then an error is printed.
|
||||
*/
|
||||
if (!lvmlockd_use()) {
|
||||
/*
|
||||
* Some reporting/display commands have the --shared option
|
||||
* (like --foreign) to allow them to reveal lockd VGs that
|
||||
* are otherwise ignored. The --shared option must only be
|
||||
* permitted in commands that read the VG for report or display,
|
||||
* not any that write the VG or activate LVs.
|
||||
*/
|
||||
if (cmd->include_shared_vgs)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Some commands want the error printed by vg_read, others by ignore_vg.
|
||||
* Those using ignore_vg may choose to skip the error.
|
||||
*/
|
||||
if (cmd->vg_read_print_access_error) {
|
||||
log_error("Cannot access VG %s with lock type %s that requires lvmlockd.",
|
||||
vg->name, vg->lock_type);
|
||||
}
|
||||
|
||||
*failure |= FAILED_LOCK_TYPE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The lock request from lvmlockd failed. If the lock was ex,
|
||||
* we cannot continue. If the lock was sh, we could also fail
|
||||
* to continue but since the lock was sh, it means the VG is
|
||||
* only being read, and it doesn't hurt to allow reading with
|
||||
* no lock.
|
||||
*/
|
||||
if (lockd_state & LDST_FAIL) {
|
||||
if ((lockd_state & LDST_EX) || cmd->lockd_vg_enforce_sh) {
|
||||
log_error("Cannot access VG %s due to failed lock.", vg->name);
|
||||
*failure |= FAILED_LOCK_MODE;
|
||||
return 0;
|
||||
} else {
|
||||
log_warn("Reading VG %s without a lock.", vg->name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -4497,7 +4848,7 @@ static int _access_vg_systemid(struct cmd_context *cmd, struct volume_group *vg)
|
||||
/*
|
||||
* A host can access a VG if the VG's system_id is in extra_system_ids list.
|
||||
*/
|
||||
if (cmd->system_id && _allow_system_id(cmd, vg->system_id))
|
||||
if (cmd->system_id && _allow_extra_system_id(cmd, vg->system_id))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
@@ -4521,18 +4872,16 @@ static int _access_vg_systemid(struct cmd_context *cmd, struct volume_group *vg)
|
||||
}
|
||||
|
||||
/*
|
||||
* Some commands always produce an error when accessing foreign VG.
|
||||
* Some commands want the error printed by vg_read, others by ignore_vg.
|
||||
* Those using ignore_vg may choose to skip the error.
|
||||
*/
|
||||
if (cmd->error_foreign_vgs) {
|
||||
if (cmd->vg_read_print_access_error) {
|
||||
log_error("Cannot access VG %s with system ID %s with local system ID %s.",
|
||||
vg->name, vg->system_id, cmd->system_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When include_foreign_vgs is 0 and error_foreign_vgs is 0,
|
||||
* the result is to silently ignore foreign vgs.
|
||||
*/
|
||||
/* Silently ignore foreign vgs. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -4540,7 +4889,8 @@ static int _access_vg_systemid(struct cmd_context *cmd, struct volume_group *vg)
|
||||
/*
|
||||
* FIXME: move _vg_bad_status_bits() checks in here.
|
||||
*/
|
||||
static int _vg_access_permitted(struct cmd_context *cmd, struct volume_group *vg, uint32_t *failure)
|
||||
static int _vg_access_permitted(struct cmd_context *cmd, struct volume_group *vg,
|
||||
uint32_t lockd_state, uint32_t *failure)
|
||||
{
|
||||
if (!is_real_vg(vg->name)) {
|
||||
/* Disallow use of LVM1 orphans when a host system ID is set. */
|
||||
@@ -4556,8 +4906,8 @@ static int _vg_access_permitted(struct cmd_context *cmd, struct volume_group *vg
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_access_vg_lock_type(cmd, vg)) {
|
||||
*failure |= FAILED_LOCK_TYPE;
|
||||
if (!_access_vg_lock_type(cmd, vg, lockd_state, failure)) {
|
||||
/* Either FAILED_LOCK_TYPE or FAILED_LOCK_MODE were set. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4582,7 +4932,8 @@ static int _vg_access_permitted(struct cmd_context *cmd, struct volume_group *vg
|
||||
*/
|
||||
static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const char *vg_name,
|
||||
const char *vgid, uint32_t lock_flags,
|
||||
uint64_t status_flags, uint32_t misc_flags)
|
||||
uint64_t status_flags, uint32_t misc_flags,
|
||||
uint32_t lockd_state)
|
||||
{
|
||||
struct volume_group *vg = NULL;
|
||||
int consistent = 1;
|
||||
@@ -4628,7 +4979,7 @@ static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const cha
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_vg_access_permitted(cmd, vg, &failure))
|
||||
if (!_vg_access_permitted(cmd, vg, lockd_state, &failure))
|
||||
goto bad;
|
||||
|
||||
/* consistent == 0 when VG is not found, but failed == FAILED_NOTFOUND */
|
||||
@@ -4704,7 +5055,7 @@ bad_no_unlock:
|
||||
* *consistent = 1.
|
||||
*/
|
||||
struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
|
||||
const char *vgid, uint32_t flags)
|
||||
const char *vgid, uint32_t flags, uint32_t lockd_state)
|
||||
{
|
||||
uint64_t status = UINT64_C(0);
|
||||
uint32_t lock_flags = LCK_VG_READ;
|
||||
@@ -4717,7 +5068,7 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
|
||||
if (flags & READ_ALLOW_EXPORTED)
|
||||
status &= ~EXPORTED_VG;
|
||||
|
||||
return _vg_lock_and_read(cmd, vg_name, vgid, lock_flags, status, flags);
|
||||
return _vg_lock_and_read(cmd, vg_name, vgid, lock_flags, status, flags, lockd_state);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4726,9 +5077,9 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
|
||||
* request the new metadata to be written and committed).
|
||||
*/
|
||||
struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_name,
|
||||
const char *vgid, uint32_t flags)
|
||||
const char *vgid, uint32_t flags, uint32_t lockd_state)
|
||||
{
|
||||
return vg_read(cmd, vg_name, vgid, flags | READ_FOR_UPDATE);
|
||||
return vg_read(cmd, vg_name, vgid, flags | READ_FOR_UPDATE, lockd_state);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5160,3 +5511,21 @@ const struct logical_volume *lv_ondisk(const struct logical_volume *lv)
|
||||
|
||||
return lvl->lv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a lock_type uses lvmlockd.
|
||||
* If not (none, clvm), return 0.
|
||||
* If so (dlm, sanlock), return 1.
|
||||
*/
|
||||
|
||||
int is_lockd_type(const char *lock_type)
|
||||
{
|
||||
if (!lock_type)
|
||||
return 0;
|
||||
if (!strcmp(lock_type, "dlm"))
|
||||
return 1;
|
||||
if (!strcmp(lock_type, "sanlock"))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -460,6 +460,8 @@ struct volume_group *import_vg_from_buffer(const char *buf,
|
||||
struct format_instance *fid);
|
||||
struct volume_group *import_vg_from_config_tree(const struct dm_config_tree *cft,
|
||||
struct format_instance *fid);
|
||||
struct volume_group *import_vg_from_lvmetad_config_tree(const struct dm_config_tree *cft,
|
||||
struct format_instance *fid);
|
||||
|
||||
/*
|
||||
* Mirroring functions
|
||||
@@ -483,6 +485,7 @@ int lv_is_merging_thin_snapshot(const struct logical_volume *lv);
|
||||
int pool_has_message(const struct lv_segment *seg,
|
||||
const struct logical_volume *lv, uint32_t device_id);
|
||||
int pool_below_threshold(const struct lv_segment *pool_seg);
|
||||
int pool_check_overprovisioning(const struct logical_volume *lv);
|
||||
int create_pool(struct logical_volume *lv, const struct segment_type *segtype,
|
||||
struct alloc_handle *ah, uint32_t stripes, uint32_t stripe_size);
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ uint32_t adjusted_mirror_region_size(uint32_t extent_size, uint32_t extents,
|
||||
uint64_t region_max;
|
||||
uint64_t region_min, region_min_pow2;
|
||||
|
||||
region_max = (1 << (ffs((int)extents) - 1)) * (uint64_t) (1 << (ffs((int)extent_size) - 1));
|
||||
region_max = (UINT64_C(1) << (ffs((int)extents) - 1)) * (UINT64_C(1) << (ffs((int)extent_size) - 1));
|
||||
|
||||
if (region_max < UINT32_MAX && region_size > region_max) {
|
||||
region_size = (uint32_t) region_max;
|
||||
@@ -368,7 +368,11 @@ static int _init_mirror_log(struct cmd_context *cmd,
|
||||
backup(log_lv->vg);
|
||||
|
||||
/* Wait for events following any deactivation before reactivating */
|
||||
sync_local_dev_names(cmd);
|
||||
if (!sync_local_dev_names(cmd)) {
|
||||
log_error("Aborting. Failed to sync local devices before initialising mirror log %s.",
|
||||
display_lvname(log_lv));
|
||||
goto revert_new_lv;
|
||||
}
|
||||
|
||||
if (!activate_lv(cmd, log_lv)) {
|
||||
log_error("Aborting. Failed to activate mirror log.");
|
||||
@@ -484,7 +488,11 @@ static int _delete_lv(struct logical_volume *mirror_lv, struct logical_volume *l
|
||||
return_0;
|
||||
|
||||
/* FIXME Is this superfluous now? */
|
||||
sync_local_dev_names(cmd);
|
||||
if (!sync_local_dev_names(cmd)) {
|
||||
log_error("Failed to sync local devices when reactivating %s.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!deactivate_lv(cmd, lv))
|
||||
return_0;
|
||||
@@ -1163,7 +1171,7 @@ static int _remove_mirror_images(struct logical_volume *lv,
|
||||
if (removed)
|
||||
*removed = old_area_count - new_area_count;
|
||||
|
||||
log_very_verbose("%" PRIu32 " image(s) removed from %s",
|
||||
log_very_verbose(FMTu32 " image(s) removed from %s",
|
||||
old_area_count - new_area_count, lv->name);
|
||||
|
||||
return 1;
|
||||
@@ -1904,7 +1912,7 @@ static int _form_mirror(struct cmd_context *cmd, struct alloc_handle *ah,
|
||||
}
|
||||
|
||||
if (!_create_mimage_lvs(ah, mirrors, stripes, stripe_size, lv, img_lvs, log))
|
||||
return 0;
|
||||
return_0;
|
||||
|
||||
if (!lv_add_mirror_lvs(lv, img_lvs, mirrors,
|
||||
MIRROR_IMAGE | (lv->status & LOCKED),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user