mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-23 23:33:15 +03:00
Compare commits
1019 Commits
dev-lvmguy
...
beta7_5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49738f43c0 | ||
|
|
9f85f61010 | ||
|
|
239f422039 | ||
|
|
67af3c37be | ||
|
|
a9442385c4 | ||
|
|
8c9cd10b8b | ||
|
|
72542059dd | ||
|
|
a843fc6d40 | ||
|
|
4beed60c08 | ||
|
|
4049c1e480 | ||
|
|
8449314da2 | ||
|
|
63ad057028 | ||
|
|
e720464330 | ||
|
|
24036afef9 | ||
|
|
c78fa1a1bc | ||
|
|
faa8b9022c | ||
|
|
729bafef7a | ||
|
|
590b028632 | ||
|
|
8150d00f36 | ||
|
|
060065926f | ||
|
|
70babe8a28 | ||
|
|
c36e09664f | ||
|
|
a9672246f3 | ||
|
|
ff571884e9 | ||
|
|
475138bceb | ||
|
|
4a8af199c2 | ||
|
|
bdabf5db72 | ||
|
|
6a5f21b34e | ||
|
|
d608be103c | ||
|
|
374bb5d18a | ||
|
|
031d6c25ff | ||
|
|
223fb7b075 | ||
|
|
a746741971 | ||
|
|
120faf2a58 | ||
|
|
990bca0dc6 | ||
|
|
3406472db7 | ||
|
|
1bd733c9f6 | ||
|
|
238c7f982e | ||
|
|
fcb81147cb | ||
|
|
1915b73783 | ||
|
|
ee79e621fb | ||
|
|
d203275a3b | ||
|
|
9e8a996222 | ||
|
|
0126b0b3ed | ||
|
|
458928612c | ||
|
|
e33f88e28d | ||
|
|
be570bbf9e | ||
|
|
f59b4be110 | ||
|
|
37336e41be | ||
|
|
d24a1a3f0a | ||
|
|
f7258955bd | ||
|
|
2a1eae5d6f | ||
|
|
50ee0a4adb | ||
|
|
955a26584e | ||
|
|
1d3e407c8f | ||
|
|
cb809c4596 | ||
|
|
53bbe2888e | ||
|
|
7246f476a5 | ||
|
|
0785d1c390 | ||
|
|
85d2c49d14 | ||
|
|
8b77d62b7f | ||
|
|
373058a32a | ||
|
|
e6293c2c8c | ||
|
|
eff181c959 | ||
|
|
54752c2305 | ||
|
|
b4753c044f | ||
|
|
26493424ae | ||
|
|
0282fd1332 | ||
|
|
b9a019a08b | ||
|
|
66f6a0e687 | ||
|
|
2dd1b9f97d | ||
|
|
89615f3045 | ||
|
|
7e46192f67 | ||
|
|
e78d985cdf | ||
|
|
e8c4bf56fe | ||
|
|
e6aa7d323d | ||
|
|
fca8e25929 | ||
|
|
8e8ac286b4 | ||
|
|
7d9770b9a2 | ||
|
|
1996230460 | ||
|
|
de7897a864 | ||
|
|
e2884dcdb7 | ||
|
|
544a53a42b | ||
|
|
2e4787bfc8 | ||
|
|
1829eeb171 | ||
|
|
c7488e3c4a | ||
|
|
3bf9606383 | ||
|
|
4f43f18f0a | ||
|
|
5b7f197397 | ||
|
|
018141c97f | ||
|
|
4d7813e57c | ||
|
|
605c60208f | ||
|
|
884fafcc30 | ||
|
|
56baa90320 | ||
|
|
6bb20ee09e | ||
|
|
541356430c | ||
|
|
2f4d91fd69 | ||
|
|
f58c5e6b30 | ||
|
|
0311d0132c | ||
|
|
c946c97402 | ||
|
|
84a6f51318 | ||
|
|
24a1501b0d | ||
|
|
383b6f5fcc | ||
|
|
633dd7ff9b | ||
|
|
580624fad6 | ||
|
|
a8190f7efa | ||
|
|
dd2157534b | ||
|
|
38a90e7669 | ||
|
|
6bfc526dcd | ||
|
|
aadb8a7405 | ||
|
|
27082bf77e | ||
|
|
a2903c80cd | ||
|
|
9a77c5369c | ||
|
|
3c30741a19 | ||
|
|
7028ad4ec0 | ||
|
|
8de750c6aa | ||
|
|
04f98de9ee | ||
|
|
a1a019784b | ||
|
|
4aeeae77bd | ||
|
|
651cfc2b78 | ||
|
|
2ef8af25e2 | ||
|
|
0b13852a5b | ||
|
|
13427578c9 | ||
|
|
d89ca2087e | ||
|
|
8b0ea9fba6 | ||
|
|
9f0b653d5a | ||
|
|
659a339233 | ||
|
|
4c29f177a0 | ||
|
|
d664e63d55 | ||
|
|
2493509dbe | ||
|
|
1c8b27f554 | ||
|
|
68297b7186 | ||
|
|
34dd8d0a91 | ||
|
|
81c44790d5 | ||
|
|
d557b335cf | ||
|
|
e2adc28cff | ||
|
|
0fef6a6ecc | ||
|
|
f2fd4b8a1f | ||
|
|
95bd5605a8 | ||
|
|
497cca7eca | ||
|
|
54f78feedd | ||
|
|
76408e53ae | ||
|
|
be19e74d30 | ||
|
|
dac578a775 | ||
|
|
04732ce74b | ||
|
|
a6c95a2374 | ||
|
|
f68622abe9 | ||
|
|
83a9a7bdb2 | ||
|
|
6500afc0ca | ||
|
|
c69b7ecc96 | ||
|
|
615ef1e2d2 | ||
|
|
cf69a0cd7f | ||
|
|
06e892fb33 | ||
|
|
e6c5dd6865 | ||
|
|
247efdebdb | ||
|
|
76f3792287 | ||
|
|
64d8e2c727 | ||
|
|
ead0bd9cb0 | ||
|
|
66fc13b2ec | ||
|
|
f3af4128b0 | ||
|
|
0e43107c87 | ||
|
|
1ee3e7997e | ||
|
|
50fd61d91f | ||
|
|
e4cdc051a9 | ||
|
|
778e846e96 | ||
|
|
a27759b647 | ||
|
|
b75eceab41 | ||
|
|
e748a5d5f4 | ||
|
|
99c5a3ae46 | ||
|
|
51da710f5a | ||
|
|
569d69b3d2 | ||
|
|
059a6b1d90 | ||
|
|
990af7548a | ||
|
|
a38aefdfc8 | ||
|
|
3bcb12e7d1 | ||
|
|
7904ecb462 | ||
|
|
9ba4d45109 | ||
|
|
56b8afe19d | ||
|
|
f7aed9a94c | ||
|
|
e12a7e881d | ||
|
|
5afb65325d | ||
|
|
135f520f32 | ||
|
|
bc251f4ff6 | ||
|
|
b8769751f6 | ||
|
|
eff96d839e | ||
|
|
aa34c23807 | ||
|
|
195acdac8c | ||
|
|
903e03c56c | ||
|
|
0892767b8a | ||
|
|
83ebfa772c | ||
|
|
1583641322 | ||
|
|
9510e2c256 | ||
|
|
a9dbabe07e | ||
|
|
12884008fa | ||
|
|
02543bad1c | ||
|
|
a8c56a5251 | ||
|
|
4e5a855f3f | ||
|
|
7e497a951e | ||
|
|
cd08eabbfa | ||
|
|
f7e62d9f81 | ||
|
|
9a3761e86e | ||
|
|
3619a68693 | ||
|
|
efaf3c3bf9 | ||
|
|
0e4e6a6f67 | ||
|
|
42458e6278 | ||
|
|
41ec995377 | ||
|
|
4f37599326 | ||
|
|
4144520e5c | ||
|
|
6c4800546c | ||
|
|
733733c8a7 | ||
|
|
2aa67cc946 | ||
|
|
9385981a9d | ||
|
|
fff780035d | ||
|
|
46127e673d | ||
|
|
70df59b224 | ||
|
|
0fdbaa803f | ||
|
|
6af1830eff | ||
|
|
6f860e2bd5 | ||
|
|
20a492f7ee | ||
|
|
63875e7591 | ||
|
|
0ad98cabde | ||
|
|
668879d2e1 | ||
|
|
2e09783302 | ||
|
|
49734114b3 | ||
|
|
950d9d6ee7 | ||
|
|
f7974aee2e | ||
|
|
c870a82621 | ||
|
|
984929a001 | ||
|
|
7f9e2c1db8 | ||
|
|
324e8389dc | ||
|
|
6f448c5a38 | ||
|
|
ec43efbb20 | ||
|
|
3ed065de37 | ||
|
|
8152f0d72c | ||
|
|
f8047f4736 | ||
|
|
b93c66dc2d | ||
|
|
ac877b3065 | ||
|
|
dee8abfdde | ||
|
|
cef3841d73 | ||
|
|
3cd5e8a041 | ||
|
|
515b5f866e | ||
|
|
321f62bf92 | ||
|
|
f94fa47b52 | ||
|
|
c502f8a722 | ||
|
|
59b4868ac3 | ||
|
|
3634e12cce | ||
|
|
6d45445391 | ||
|
|
4f47e268cc | ||
|
|
0035b31cdb | ||
|
|
f2565aee03 | ||
|
|
5bd85668dd | ||
|
|
20f990b6ce | ||
|
|
6821379586 | ||
|
|
73b040eb49 | ||
|
|
49aa4b2e1e | ||
|
|
972241c74c | ||
|
|
680750e3c2 | ||
|
|
5e7d4d9d15 | ||
|
|
1e57e60613 | ||
|
|
3ac7ce605a | ||
|
|
b720dea9f0 | ||
|
|
c80722aefe | ||
|
|
a84fa69f28 | ||
|
|
e33781e59f | ||
|
|
8824bc7ece | ||
|
|
c2e3b0e448 | ||
|
|
f61a38e85a | ||
|
|
d23e948216 | ||
|
|
58bdaa31f0 | ||
|
|
b6491d88a6 | ||
|
|
f6061ba62e | ||
|
|
427899ddce | ||
|
|
c4ab7d2dbd | ||
|
|
0fd2ba033f | ||
|
|
ed38939a93 | ||
|
|
c7ee26ce5a | ||
|
|
909b8cb303 | ||
|
|
b0277370cf | ||
|
|
2ec8656bea | ||
|
|
b2ef256910 | ||
|
|
63d6ce95db | ||
|
|
a9532b189c | ||
|
|
844545411f | ||
|
|
4e23a2b9b8 | ||
|
|
5deba027eb | ||
|
|
fc8b7efc6f | ||
|
|
a1c2d9c0f3 | ||
|
|
4ca49a0501 | ||
|
|
493c53d090 | ||
|
|
b27e956d35 | ||
|
|
35ebed75c6 | ||
|
|
7bfdb5f77f | ||
|
|
8d8c02317f | ||
|
|
a34482feab | ||
|
|
cbdc8fd4a6 | ||
|
|
8d3afaa53c | ||
|
|
7ced9ef3df | ||
|
|
e8a9ae7e80 | ||
|
|
73a88ab3d3 | ||
|
|
aaed82738a | ||
|
|
de7f7b96db | ||
|
|
1a669b3e68 | ||
|
|
333af9b13a | ||
|
|
a5bca5e240 | ||
|
|
c885633e02 | ||
|
|
ca7e20b7ca | ||
|
|
545e11a3d7 | ||
|
|
44f5287664 | ||
|
|
cf510897f1 | ||
|
|
1d171345f8 | ||
|
|
4fa7e1cd49 | ||
|
|
acd008298e | ||
|
|
83a8021515 | ||
|
|
cf88dfb1db | ||
|
|
8937c4b481 | ||
|
|
cc6af10a4d | ||
|
|
6d94578955 | ||
|
|
08442ab71e | ||
|
|
10d91d213f | ||
|
|
b7a3b06994 | ||
|
|
5f12c37f23 | ||
|
|
585edebccd | ||
|
|
9921c62234 | ||
|
|
1ac76d2e16 | ||
|
|
6e983bf400 | ||
|
|
6a8fd4fa6e | ||
|
|
3698eaa2d2 | ||
|
|
8d97ca433c | ||
|
|
23cc65e537 | ||
|
|
2f5a3c2bbe | ||
|
|
f6485616cd | ||
|
|
6544fb43d9 | ||
|
|
a954b32dcc | ||
|
|
426dc7836c | ||
|
|
ff941ffc16 | ||
|
|
a063c201df | ||
|
|
e2be3fa0aa | ||
|
|
609da6fb50 | ||
|
|
fc9f3ccec3 | ||
|
|
f7baa67a0a | ||
|
|
e8d78c2cdb | ||
|
|
9d33366092 | ||
|
|
f5d61515c2 | ||
|
|
711a04a972 | ||
|
|
e5b470a3f1 | ||
|
|
31820e1e22 | ||
|
|
4849e8cd6d | ||
|
|
77e3b460aa | ||
|
|
b5321001f8 | ||
|
|
38eba9f5ea | ||
|
|
ea6f399454 | ||
|
|
f8bf2d7b7d | ||
|
|
c4856caebb | ||
|
|
fc1030bb22 | ||
|
|
9eb6cad8dc | ||
|
|
0fa2a78dce | ||
|
|
a56fa1558b | ||
|
|
261c73a997 | ||
|
|
929c1333ca | ||
|
|
286a79d94d | ||
|
|
8b951f99da | ||
|
|
abb449bca0 | ||
|
|
bd084028d1 | ||
|
|
138a27570b | ||
|
|
c2ed40a74f | ||
|
|
a7e7a00cab | ||
|
|
64a31ab3cd | ||
|
|
71958bc0f1 | ||
|
|
366ec35612 | ||
|
|
3738f6e8ae | ||
|
|
051a8e2af1 | ||
|
|
2f43f28d5e | ||
|
|
b64769754b | ||
|
|
a97daa18d1 | ||
|
|
b6556dce8b | ||
|
|
aa8d8bc8b5 | ||
|
|
0800dcfdc4 | ||
|
|
12b0101e94 | ||
|
|
021b391a02 | ||
|
|
d11e2b6057 | ||
|
|
264d90e7e5 | ||
|
|
f9f3da9e78 | ||
|
|
6435b49153 | ||
|
|
f9aa2941cf | ||
|
|
5a933d4bee | ||
|
|
5536aea0df | ||
|
|
976666d216 | ||
|
|
9a5bcd4392 | ||
|
|
812060a118 | ||
|
|
089b5052e6 | ||
|
|
874e5d72f4 | ||
|
|
6b11de1329 | ||
|
|
2245a7ad8d | ||
|
|
ee5ec1b870 | ||
|
|
74ccfe851b | ||
|
|
e1c24bd5a2 | ||
|
|
1349b79728 | ||
|
|
d294d7604c | ||
|
|
01df2cf464 | ||
|
|
45b7322488 | ||
|
|
b3055e992f | ||
|
|
3da548565c | ||
|
|
a975e85548 | ||
|
|
c4b5ade752 | ||
|
|
a4b61b0794 | ||
|
|
f6011184b8 | ||
|
|
74de118c6e | ||
|
|
9c25e77c17 | ||
|
|
440113e67e | ||
|
|
a11b60c445 | ||
|
|
a6052681ad | ||
|
|
482d50536a | ||
|
|
25c79a4fcd | ||
|
|
02946144ac | ||
|
|
71a360e9a3 | ||
|
|
f7912d88b1 | ||
|
|
975101d7d2 | ||
|
|
ef58c5ff55 | ||
|
|
e10221804a | ||
|
|
284ed9ee0e | ||
|
|
5d7b961997 | ||
|
|
c699a5c4b4 | ||
|
|
1f4ceb89cf | ||
|
|
0934ca0040 | ||
|
|
4738f892c2 | ||
|
|
33004fcf33 | ||
|
|
34d214c166 | ||
|
|
a56cd92a1e | ||
|
|
7251348507 | ||
|
|
01cd5c84d6 | ||
|
|
e210599fa6 | ||
|
|
dbe7cee7e9 | ||
|
|
7370d88ceb | ||
|
|
34458e0c57 | ||
|
|
05c8c3abf2 | ||
|
|
e9d464b4d3 | ||
|
|
6968c3ab9b | ||
|
|
131a8a9650 | ||
|
|
379ecbf9a9 | ||
|
|
7b9dfa9a28 | ||
|
|
fbd0f5eed2 | ||
|
|
5970f904ae | ||
|
|
8cbcb2868d | ||
|
|
8ff2a4b026 | ||
|
|
ae14d205a5 | ||
|
|
ec5d560ec5 | ||
|
|
fb543b53c0 | ||
|
|
39c16422e2 | ||
|
|
4d5b273ebe | ||
|
|
ed6a860fad | ||
|
|
423e579292 | ||
|
|
ee11aa9e75 | ||
|
|
c46d20fa92 | ||
|
|
dc8d17574c | ||
|
|
5c17cd04c8 | ||
|
|
67ada02076 | ||
|
|
32d94c2eaf | ||
|
|
687b39a12a | ||
|
|
705af407bf | ||
|
|
080052da2e | ||
|
|
ef735fd92a | ||
|
|
17c16dcafc | ||
|
|
a89b3018fb | ||
|
|
851e2ebd32 | ||
|
|
1225ce7fe8 | ||
|
|
7e77a31b96 | ||
|
|
d146b9002f | ||
|
|
1f9d567b23 | ||
|
|
ed1b3a023c | ||
|
|
1ca18f501a | ||
|
|
49588ccd98 | ||
|
|
098dedc092 | ||
|
|
b06f6b9545 | ||
|
|
ba3cb94999 | ||
|
|
74c67fbf4b | ||
|
|
32b46e4910 | ||
|
|
ecf5539ed2 | ||
|
|
ac9db4e4d5 | ||
|
|
0eb96094b0 | ||
|
|
30b3ac7dc5 | ||
|
|
0092790c7d | ||
|
|
28909d8a51 | ||
|
|
b20cfbb7b6 | ||
|
|
97639bd0a8 | ||
|
|
161ec73c96 | ||
|
|
957d6bea15 | ||
|
|
f8b6e5b414 | ||
|
|
1ab450870e | ||
|
|
de45f4884c | ||
|
|
5da1f3e7c8 | ||
|
|
983014952b | ||
|
|
55298019a3 | ||
|
|
a1ffc3f271 | ||
|
|
2fb60aa997 | ||
|
|
f5ec76537a | ||
|
|
728491fd2b | ||
|
|
d9d3f2b9e4 | ||
|
|
3fe4864f65 | ||
|
|
0b156f22a4 | ||
|
|
35b1d93813 | ||
|
|
d770851ac0 | ||
|
|
989e7b1033 | ||
|
|
c4e0eb7b49 | ||
|
|
71f5d0dac7 | ||
|
|
561b0c4381 | ||
|
|
995fbc7330 | ||
|
|
10ab8949c4 | ||
|
|
c441202fea | ||
|
|
ca261b0bee | ||
|
|
52f3709f67 | ||
|
|
c2ca6187fe | ||
|
|
671a13d295 | ||
|
|
14c3e2eccf | ||
|
|
08e5b852c2 | ||
|
|
1c9606c824 | ||
|
|
3cd47b5c9b | ||
|
|
aedc729087 | ||
|
|
5f7cfa3fa9 | ||
|
|
0083f30af5 | ||
|
|
4a06f05ef5 | ||
|
|
8f37cadce8 | ||
|
|
a11603ca6c | ||
|
|
86274842e9 | ||
|
|
cf9c955a44 | ||
|
|
55d828c35f | ||
|
|
cdff28aca6 | ||
|
|
b9da39274f | ||
|
|
c379aa5782 | ||
|
|
9dcabac9dd | ||
|
|
794f3a2b9f | ||
|
|
2066121b7c | ||
|
|
0c4067f143 | ||
|
|
8aa69243b7 | ||
|
|
8697263bde | ||
|
|
ea25c4f65c | ||
|
|
82ac3ebd7e | ||
|
|
13cb94909c | ||
|
|
b57ca2a763 | ||
|
|
83c49e9745 | ||
|
|
e15771d78d | ||
|
|
6edc4920ba | ||
|
|
302bb1bd93 | ||
|
|
529b1bceee | ||
|
|
42cd47d32e | ||
|
|
711d884c2e | ||
|
|
183d1c4674 | ||
|
|
faed63a0bb | ||
|
|
53bff262f8 | ||
|
|
3251a708e4 | ||
|
|
b5dbdbf7b2 | ||
|
|
a9649e92c9 | ||
|
|
b561f1fa8b | ||
|
|
cecd7491b5 | ||
|
|
55a66322b5 | ||
|
|
155c31a2d7 | ||
|
|
a89ce91089 | ||
|
|
b897fe6700 | ||
|
|
548a351b06 | ||
|
|
4b1da57ca1 | ||
|
|
529aec7f25 | ||
|
|
1661e545cb | ||
|
|
ec71d08878 | ||
|
|
ac258b7dd7 | ||
|
|
0f57876233 | ||
|
|
1d25a3693d | ||
|
|
45fa428bf1 | ||
|
|
177fa80f1a | ||
|
|
3261261bfe | ||
|
|
d4de7934f8 | ||
|
|
c3475af809 | ||
|
|
b12f707812 | ||
|
|
22c0c34d60 | ||
|
|
a60b66f230 | ||
|
|
83f6e93628 | ||
|
|
222b5f0229 | ||
|
|
30aa383e26 | ||
|
|
676b401294 | ||
|
|
ebf57159de | ||
|
|
199d2aafec | ||
|
|
81952f56fd | ||
|
|
c5bac82b43 | ||
|
|
081b86109c | ||
|
|
8ac2028a75 | ||
|
|
264fed1c9f | ||
|
|
dd59f7b2c7 | ||
|
|
9245a760db | ||
|
|
b61b32bbc3 | ||
|
|
09b3914f5d | ||
|
|
fe644e4c9e | ||
|
|
7b09bf2156 | ||
|
|
987d0aae66 | ||
|
|
9cbcbc1c22 | ||
|
|
cfc4e2bc60 | ||
|
|
a03405fa81 | ||
|
|
d5c9ccbe6e | ||
|
|
e52772d65f | ||
|
|
1e3259e728 | ||
|
|
e905a20a60 | ||
|
|
88e2be7a33 | ||
|
|
8939131600 | ||
|
|
ba37ebff8b | ||
|
|
28f4cb7e07 | ||
|
|
db1e7102cd | ||
|
|
07eb7a5830 | ||
|
|
b7c6c685fa | ||
|
|
212134df70 | ||
|
|
6eeb5528f5 | ||
|
|
54fad845c9 | ||
|
|
d6c0de6fc7 | ||
|
|
649c8649f7 | ||
|
|
da2f53d1b1 | ||
|
|
405139e3b8 | ||
|
|
4f8d347171 | ||
|
|
bf0db4876c | ||
|
|
47a14884d6 | ||
|
|
3a7bbc8b08 | ||
|
|
1b1d65372c | ||
|
|
fd2faaa16e | ||
|
|
0609cdb9ea | ||
|
|
d3bb140f89 | ||
|
|
b31dc66628 | ||
|
|
09476171a6 | ||
|
|
33dee813b5 | ||
|
|
bb4e73c40b | ||
|
|
b1f23ffa94 | ||
|
|
b0e8cec1e7 | ||
|
|
5077ae19bc | ||
|
|
0d8447bf59 | ||
|
|
c6cf08a274 | ||
|
|
dc49ae519e | ||
|
|
904539476a | ||
|
|
3fbf02dc82 | ||
|
|
c9392a840d | ||
|
|
d164e8ab72 | ||
|
|
6dc62c9fb6 | ||
|
|
87a9684d66 | ||
|
|
94525e2f44 | ||
|
|
b408b1b3b9 | ||
|
|
27c2f09e32 | ||
|
|
19bc4d3349 | ||
|
|
f2b6c424d6 | ||
|
|
a49d4453e9 | ||
|
|
65e50087b9 | ||
|
|
2d90f759d9 | ||
|
|
4230ac7674 | ||
|
|
d96e9182e9 | ||
|
|
68c87b9616 | ||
|
|
7f8e9a0b6d | ||
|
|
81a229f2a5 | ||
|
|
8be7ae2733 | ||
|
|
846bca4cb1 | ||
|
|
f36f353789 | ||
|
|
939a2731ed | ||
|
|
835dab97ff | ||
|
|
fa904b53be | ||
|
|
0ec52dddce | ||
|
|
c289355a3a | ||
|
|
02a13a5a18 | ||
|
|
6cf2a0281b | ||
|
|
120d35f9af | ||
|
|
2b15d5e7b3 | ||
|
|
fc167bd3f0 | ||
|
|
91b04abf05 | ||
|
|
77faac8740 | ||
|
|
43b3d54855 | ||
|
|
69e9b85700 | ||
|
|
0b6d132759 | ||
|
|
7c233c6c0c | ||
|
|
c35b290fa4 | ||
|
|
3d95cfb367 | ||
|
|
b90fc3a56e | ||
|
|
1ef3fdccf5 | ||
|
|
02b7f77bd8 | ||
|
|
0ac7ead922 | ||
|
|
da9d0e03ce | ||
|
|
120f65f672 | ||
|
|
200a14caa4 | ||
|
|
35bf6da8e2 | ||
|
|
f08f70276c | ||
|
|
1ae50fd95b | ||
|
|
40512beb47 | ||
|
|
0d7f9b2c94 | ||
|
|
52f42140a7 | ||
|
|
3f6c50297f | ||
|
|
f72d80afc5 | ||
|
|
7c5cb13b22 | ||
|
|
d728750eb2 | ||
|
|
02a70e5667 | ||
|
|
44e51ea5fa | ||
|
|
87e201460a | ||
|
|
039bd945e2 | ||
|
|
e9e52d2b4b | ||
|
|
2bf92e7399 | ||
|
|
5b0df241f0 | ||
|
|
76f5b05eff | ||
|
|
40fb6c998f | ||
|
|
33f50a342d | ||
|
|
81523ab68a | ||
|
|
2bf8cc62cf | ||
|
|
1ae8247af3 | ||
|
|
5ef32227ec | ||
|
|
6456e773bd | ||
|
|
234fe53ca3 | ||
|
|
7c93e7a7b3 | ||
|
|
8afc6c7f4b | ||
|
|
4609d0fa3a | ||
|
|
d452c035c6 | ||
|
|
45113c8f5a | ||
|
|
0acdd3c62b | ||
|
|
96d7d0a33e | ||
|
|
b6b280267b | ||
|
|
6e6d253b1a | ||
|
|
d92c105db2 | ||
|
|
906db728d6 | ||
|
|
c4b7411565 | ||
|
|
de06396046 | ||
|
|
ee6bfeb8e3 | ||
|
|
058347321f | ||
|
|
feefe49324 | ||
|
|
187381a9a2 | ||
|
|
993dfa4368 | ||
|
|
7e35a16440 | ||
|
|
e4eeb15926 | ||
|
|
634e0db26d | ||
|
|
56855c23e1 | ||
|
|
0b00f742e3 | ||
|
|
b7ab3f673c | ||
|
|
be04ea1e35 | ||
|
|
1f8e695802 | ||
|
|
2d82b2c64f | ||
|
|
d076caf473 | ||
|
|
c7abdefa31 | ||
|
|
ba772c0bca | ||
|
|
5bad234119 | ||
|
|
c7e7baaf23 | ||
|
|
36658a671b | ||
|
|
045f2e10ba | ||
|
|
fb5a7db66d | ||
|
|
ba7d33982e | ||
|
|
c62279a755 | ||
|
|
17fa1a7ffb | ||
|
|
e89ceac351 | ||
|
|
0b8c30c109 | ||
|
|
9ab0f463cc | ||
|
|
6433dda7b8 | ||
|
|
fa7a2f4be4 | ||
|
|
ba90e16505 | ||
|
|
008f710203 | ||
|
|
df2740f126 | ||
|
|
2db89d143e | ||
|
|
0525d49da3 | ||
|
|
e2b0745882 | ||
|
|
92e804fc50 | ||
|
|
67abf45576 | ||
|
|
d2c9c814e7 | ||
|
|
22f8881a64 | ||
|
|
4ab20322fe | ||
|
|
5370eeecea | ||
|
|
ba71cb5dd7 | ||
|
|
9aad6c2c52 | ||
|
|
4d9627f20c | ||
|
|
c142492e91 | ||
|
|
6bf8d9e207 | ||
|
|
4f9a6168c1 | ||
|
|
38397f99aa | ||
|
|
f8686d0e75 | ||
|
|
549e3c8f9d | ||
|
|
bb56225b95 | ||
|
|
f00be261ba | ||
|
|
9cd94f26d0 | ||
|
|
4d8f5c80a7 | ||
|
|
24a23acc3d | ||
|
|
ca8703cfd8 | ||
|
|
6dcbb5b2f8 | ||
|
|
a84fdddb2a | ||
|
|
38bb2f8ceb | ||
|
|
23f5ef4345 | ||
|
|
ef8a2a9054 | ||
|
|
96103d0e36 | ||
|
|
ff5f6748df | ||
|
|
1c1fd6c366 | ||
|
|
32d37d00cb | ||
|
|
82f6cda966 | ||
|
|
f1ff8ff0d0 | ||
|
|
756c72902f | ||
|
|
73f8f0bbd0 | ||
|
|
18ed528f5d | ||
|
|
8fd2f136bc | ||
|
|
0524b1bf67 | ||
|
|
15716f65ce | ||
|
|
d46bdba332 | ||
|
|
760728110a | ||
|
|
12d0a194ca | ||
|
|
4104543508 | ||
|
|
5c211db015 | ||
|
|
2dc6180f8d | ||
|
|
e222a34b69 | ||
|
|
ef17d95063 | ||
|
|
853502e5d7 | ||
|
|
c18e297e77 | ||
|
|
c5a49599ba | ||
|
|
df9da9edf5 | ||
|
|
e2200fd050 | ||
|
|
c6207f5d9c | ||
|
|
4302b7ff6b | ||
|
|
50a7923438 | ||
|
|
ab416445c8 | ||
|
|
a54698d43c | ||
|
|
c5a77cc1c0 | ||
|
|
a9ffa811fc | ||
|
|
080a2608e0 | ||
|
|
57f2e83d6a | ||
|
|
5b030139d3 | ||
|
|
0da1ff42d1 | ||
|
|
2c599b7baa | ||
|
|
5c8af8d21a | ||
|
|
026f3cfde2 | ||
|
|
f6349180e8 | ||
|
|
aa6421921c | ||
|
|
7d41d2dab2 | ||
|
|
f0b4d18f93 | ||
|
|
6750f06e10 | ||
|
|
b2bd38fa9e | ||
|
|
3482a01e22 | ||
|
|
6335467552 | ||
|
|
4a39e65b62 | ||
|
|
c50a23e918 | ||
|
|
1e76b72b98 | ||
|
|
b94cf39eef | ||
|
|
fef254ffff | ||
|
|
e5495863a2 | ||
|
|
3b4df2abf0 | ||
|
|
aef2aee6a4 | ||
|
|
d0d9519149 | ||
|
|
685df1d2c5 | ||
|
|
08e6b6f2e7 | ||
|
|
66c887d0f3 | ||
|
|
22e9960697 | ||
|
|
64aa6e1f2d | ||
|
|
7a93ed9d04 | ||
|
|
a905e922e9 | ||
|
|
f9f08fc720 | ||
|
|
8d402d76d0 | ||
|
|
46fda6281c | ||
|
|
a14dbe1ea6 | ||
|
|
18810a4c16 | ||
|
|
147bc80dba | ||
|
|
c7a484195a | ||
|
|
4968eb6503 | ||
|
|
a6f2d698a9 | ||
|
|
ea5ed93ea5 | ||
|
|
e1140134c6 | ||
|
|
5ed11e012e | ||
|
|
5380bd39ca | ||
|
|
2ee2685688 | ||
|
|
782002245b | ||
|
|
7fc0905843 | ||
|
|
72ecb99e54 | ||
|
|
c863507d08 | ||
|
|
cff86c9093 | ||
|
|
0479dfcc54 | ||
|
|
68dd67f21c | ||
|
|
540f6858b5 | ||
|
|
b61e791a4f | ||
|
|
d0986f9482 | ||
|
|
112cb0dc28 | ||
|
|
0d3d7fdcf2 | ||
|
|
5d6b89ef3b | ||
|
|
ed0b26c09e | ||
|
|
ae292bd920 | ||
|
|
6c85a90723 | ||
|
|
852592066c | ||
|
|
96e1bc9b44 | ||
|
|
b41d81ed31 | ||
|
|
e241ec2244 | ||
|
|
16e1f1a94c | ||
|
|
7a68c42b26 | ||
|
|
37ccc2e118 | ||
|
|
4192fe1ab2 | ||
|
|
d5c743d7bb | ||
|
|
11814d63e8 | ||
|
|
b753656d50 | ||
|
|
f7e87611fc | ||
|
|
1fb0e1900e | ||
|
|
954a9731e0 | ||
|
|
65c3364ad8 | ||
|
|
3d72b7dccc | ||
|
|
13ee569f06 | ||
|
|
d79ef23a75 | ||
|
|
5d0797d4ba | ||
|
|
47a8d7475f | ||
|
|
4939053121 | ||
|
|
b525bf554e | ||
|
|
f00285d2b2 | ||
|
|
040f8d6eda | ||
|
|
66d905325c | ||
|
|
8b0cd95e73 | ||
|
|
d867cca6d9 | ||
|
|
a28f736369 | ||
|
|
5c3a71cc59 | ||
|
|
cef6dadb08 | ||
|
|
36be817a3e | ||
|
|
02f571f081 | ||
|
|
157159e487 | ||
|
|
02ada9f800 | ||
|
|
6fcf9a97bb | ||
|
|
17a5d8799f | ||
|
|
31f3fe7a22 | ||
|
|
89e46d3d83 | ||
|
|
885795e67d | ||
|
|
92bfb53dd4 | ||
|
|
4cecbeb115 | ||
|
|
5971480f55 | ||
|
|
05bebea511 | ||
|
|
76fa6c5cfb | ||
|
|
633b68b518 | ||
|
|
6913d8e995 | ||
|
|
f3654e6f8d | ||
|
|
d685dbcf22 | ||
|
|
6a57fa079e | ||
|
|
0a91d145ba | ||
|
|
c2866e799d | ||
|
|
d8cffcaae7 | ||
|
|
30abca7be2 | ||
|
|
edce87f3fb | ||
|
|
66bac98fc2 | ||
|
|
59156de92b | ||
|
|
e0d7d10600 | ||
|
|
daaf862257 | ||
|
|
9de53d4b59 | ||
|
|
f1571e2d46 | ||
|
|
bd28d06298 | ||
|
|
a24e4655eb | ||
|
|
20a6c8d8e5 | ||
|
|
98d264faf4 | ||
|
|
321902a9b5 | ||
|
|
8df5d06f9a | ||
|
|
e69ea529cc | ||
|
|
15405b1119 | ||
|
|
d2f97ce2da | ||
|
|
543ca631e9 | ||
|
|
f184886db1 | ||
|
|
8432ab4324 | ||
|
|
6c05b37ca3 | ||
|
|
35f4beeb47 | ||
|
|
cbad7caa68 | ||
|
|
b0388a4012 | ||
|
|
df3fab4d55 | ||
|
|
da49f88a03 | ||
|
|
e28feceb06 | ||
|
|
50496a164d | ||
|
|
6f1dce1572 | ||
|
|
6847776ae7 | ||
|
|
67bd53bdd8 | ||
|
|
e735abfdfd | ||
|
|
1de93a2d6d | ||
|
|
36f9e7c742 | ||
|
|
9462763bbb | ||
|
|
4ae0880ea6 | ||
|
|
6ae2b6c835 | ||
|
|
a0f180fd48 | ||
|
|
bf1cf89914 | ||
|
|
297a047fb4 | ||
|
|
52ffc15ffc | ||
|
|
e478c9c693 | ||
|
|
d004f28074 | ||
|
|
bc68ed8b1d | ||
|
|
04555ae650 | ||
|
|
e8f62085be | ||
|
|
f430bffe2a | ||
|
|
1f0520634f | ||
|
|
902d4c31fb | ||
|
|
17364ac09f | ||
|
|
0b889f8f81 | ||
|
|
40e349ff35 | ||
|
|
c943b1b1df | ||
|
|
912bc1d4e1 | ||
|
|
cacb1533a3 | ||
|
|
f0feaca9d7 | ||
|
|
b6656f171b | ||
|
|
6206ab3931 | ||
|
|
c35fc58b1f | ||
|
|
deed8abed7 | ||
|
|
7151ad23f0 | ||
|
|
0166d938af | ||
|
|
6194aeddb0 | ||
|
|
903dbf2c30 | ||
|
|
9380f9ff57 | ||
|
|
259ed95486 | ||
|
|
2ebc92681e | ||
|
|
195a1ffe13 | ||
|
|
a8c2978185 | ||
|
|
140f97a457 | ||
|
|
7f94445a1e | ||
|
|
82a89aec65 | ||
|
|
7e95110232 | ||
|
|
ec4aaaad89 | ||
|
|
1b790fde24 | ||
|
|
aaccea731e | ||
|
|
29e31d7610 | ||
|
|
aa51f4a98f | ||
|
|
e6ccd12f00 | ||
|
|
b134315df1 | ||
|
|
7f34dffa13 | ||
|
|
fa239e78c9 | ||
|
|
707a6c4d6a | ||
|
|
e5da303b43 | ||
|
|
84ccd66331 | ||
|
|
ad8cc2baea | ||
|
|
7c4cf70309 | ||
|
|
c3211e9b4f | ||
|
|
268d94c983 | ||
|
|
0bcacbba58 | ||
|
|
8cdc26add9 | ||
|
|
e0b2238886 | ||
|
|
369a2e4029 | ||
|
|
c4089e3b51 | ||
|
|
9e2e9bc5b8 | ||
|
|
a9e44426ed |
32
.gitignore
vendored
32
.gitignore
vendored
@@ -1,32 +0,0 @@
|
|||||||
*.5
|
|
||||||
*.7
|
|
||||||
*.8
|
|
||||||
*.a
|
|
||||||
*.d
|
|
||||||
*.o
|
|
||||||
*.orig
|
|
||||||
*.pc
|
|
||||||
*.pot
|
|
||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*.rej
|
|
||||||
*.so
|
|
||||||
*.so.*
|
|
||||||
*.sw*
|
|
||||||
*~
|
|
||||||
|
|
||||||
.export.sym
|
|
||||||
.exported_symbols_generated
|
|
||||||
.gdb_history
|
|
||||||
|
|
||||||
Makefile
|
|
||||||
make.tmpl
|
|
||||||
|
|
||||||
/autom4te.cache/
|
|
||||||
/autoscan.log
|
|
||||||
/config.log
|
|
||||||
/config.status
|
|
||||||
/configure.scan
|
|
||||||
/cscope.out
|
|
||||||
/tags
|
|
||||||
/tmp/
|
|
||||||
2
BUGS
Normal file
2
BUGS
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Snapshots under 2.4.18 can deadlock due to a bug in the VM system.
|
||||||
|
2.4.19-pre8 is fine.
|
||||||
4
COPYING
4
COPYING
@@ -2,7 +2,7 @@
|
|||||||
Version 2, June 1991
|
Version 2, June 1991
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
@@ -305,7 +305,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|||||||
203
COPYING.LIB
203
COPYING.LIB
@@ -1,14 +1,14 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 2.1, February 1999
|
|
||||||
|
|
||||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||||
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
[This is the first released version of the Lesser GPL. It also counts
|
[This is the first released version of the library GPL. It is
|
||||||
as the successor of the GNU Library Public License, version 2, hence
|
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||||
the version number 2.1.]
|
|
||||||
|
|
||||||
Preamble
|
Preamble
|
||||||
|
|
||||||
@@ -17,109 +17,97 @@ freedom to share and change it. By contrast, the GNU General Public
|
|||||||
Licenses are intended to guarantee your freedom to share and change
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
free software--to make sure the software is free for all its users.
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
This license, the Lesser General Public License, applies to some
|
This license, the Library General Public License, applies to some
|
||||||
specially designated software packages--typically libraries--of the
|
specially designated Free Software Foundation software, and to any
|
||||||
Free Software Foundation and other authors who decide to use it. You
|
other libraries whose authors decide to use it. You can use it for
|
||||||
can use it too, but we suggest you first think carefully about whether
|
your libraries, too.
|
||||||
this license or the ordinary General Public License is the better
|
|
||||||
strategy to use in any particular case, based on the explanations below.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom of use,
|
When we speak of free software, we are referring to freedom, not
|
||||||
not price. Our General Public Licenses are designed to make sure that
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
you have the freedom to distribute copies of free software (and charge
|
have the freedom to distribute copies of free software (and charge for
|
||||||
for this service if you wish); that you receive source code or can get
|
this service if you wish), that you receive source code or can get it
|
||||||
it if you want it; that you can change the software and use pieces of
|
if you want it, that you can change the software or use pieces of it
|
||||||
it in new free programs; and that you are informed that you can do
|
in new free programs; and that you know you can do these things.
|
||||||
these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
To protect your rights, we need to make restrictions that forbid
|
||||||
distributors to deny you these rights or to ask you to surrender these
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
rights. These restrictions translate to certain responsibilities for
|
These restrictions translate to certain responsibilities for you if
|
||||||
you if you distribute copies of the library or if you modify it.
|
you distribute copies of the library, or if you modify it.
|
||||||
|
|
||||||
For example, if you distribute copies of the library, whether gratis
|
For example, if you distribute copies of the library, whether gratis
|
||||||
or for a fee, you must give the recipients all the rights that we gave
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
you. You must make sure that they, too, receive or can get the source
|
you. You must make sure that they, too, receive or can get the source
|
||||||
code. If you link other code with the library, you must provide
|
code. If you link a program with the library, you must provide
|
||||||
complete object files to the recipients, so that they can relink them
|
complete object files to the recipients so that they can relink them
|
||||||
with the library after making changes to the library and recompiling
|
with the library, after making changes to the library and recompiling
|
||||||
it. And you must show them these terms so they know their rights.
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
We protect your rights with a two-step method: (1) we copyright the
|
Our method of protecting your rights has two steps: (1) copyright
|
||||||
library, and (2) we offer you this license, which gives you legal
|
the library, and (2) offer you this license which gives you legal
|
||||||
permission to copy, distribute and/or modify the library.
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
To protect each distributor, we want to make it very clear that
|
Also, for each distributor's protection, we want to make certain
|
||||||
there is no warranty for the free library. Also, if the library is
|
that everyone understands that there is no warranty for this free
|
||||||
modified by someone else and passed on, the recipients should know
|
library. If the library is modified by someone else and passed on, we
|
||||||
that what they have is not the original version, so that the original
|
want its recipients to know that what they have is not the original
|
||||||
author's reputation will not be affected by problems that might be
|
version, so that any problems introduced by others will not reflect on
|
||||||
introduced by others.
|
the original authors' reputations.
|
||||||
|
|
||||||
Finally, software patents pose a constant threat to the existence of
|
Finally, any free program is threatened constantly by software
|
||||||
any free program. We wish to make sure that a company cannot
|
patents. We wish to avoid the danger that companies distributing free
|
||||||
effectively restrict the users of a free program by obtaining a
|
software will individually obtain patent licenses, thus in effect
|
||||||
restrictive license from a patent holder. Therefore, we insist that
|
transforming the program into proprietary software. To prevent this,
|
||||||
any patent license obtained for a version of the library must be
|
we have made it clear that any patent must be licensed for everyone's
|
||||||
consistent with the full freedom of use specified in this license.
|
free use or not licensed at all.
|
||||||
|
|
||||||
Most GNU software, including some libraries, is covered by the
|
Most GNU software, including some libraries, is covered by the ordinary
|
||||||
ordinary GNU General Public License. This license, the GNU Lesser
|
GNU General Public License, which was designed for utility programs. This
|
||||||
General Public License, applies to certain designated libraries, and
|
license, the GNU Library General Public License, applies to certain
|
||||||
is quite different from the ordinary General Public License. We use
|
designated libraries. This license is quite different from the ordinary
|
||||||
this license for certain libraries in order to permit linking those
|
one; be sure to read it in full, and don't assume that anything in it is
|
||||||
libraries into non-free programs.
|
the same as in the ordinary license.
|
||||||
|
|
||||||
When a program is linked with a library, whether statically or using
|
The reason we have a separate public license for some libraries is that
|
||||||
a shared library, the combination of the two is legally speaking a
|
they blur the distinction we usually make between modifying or adding to a
|
||||||
combined work, a derivative of the original library. The ordinary
|
program and simply using it. Linking a program with a library, without
|
||||||
General Public License therefore permits such linking only if the
|
changing the library, is in some sense simply using the library, and is
|
||||||
entire combination fits its criteria of freedom. The Lesser General
|
analogous to running a utility program or application program. However, in
|
||||||
Public License permits more lax criteria for linking other code with
|
a textual and legal sense, the linked executable is a combined work, a
|
||||||
the library.
|
derivative of the original library, and the ordinary General Public License
|
||||||
|
treats it as such.
|
||||||
|
|
||||||
We call this license the "Lesser" General Public License because it
|
Because of this blurred distinction, using the ordinary General
|
||||||
does Less to protect the user's freedom than the ordinary General
|
Public License for libraries did not effectively promote software
|
||||||
Public License. It also provides other free software developers Less
|
sharing, because most developers did not use the libraries. We
|
||||||
of an advantage over competing non-free programs. These disadvantages
|
concluded that weaker conditions might promote sharing better.
|
||||||
are the reason we use the ordinary General Public License for many
|
|
||||||
libraries. However, the Lesser license provides advantages in certain
|
|
||||||
special circumstances.
|
|
||||||
|
|
||||||
For example, on rare occasions, there may be a special need to
|
However, unrestricted linking of non-free programs would deprive the
|
||||||
encourage the widest possible use of a certain library, so that it becomes
|
users of those programs of all benefit from the free status of the
|
||||||
a de-facto standard. To achieve this, non-free programs must be
|
libraries themselves. This Library General Public License is intended to
|
||||||
allowed to use the library. A more frequent case is that a free
|
permit developers of non-free programs to use free libraries, while
|
||||||
library does the same job as widely used non-free libraries. In this
|
preserving your freedom as a user of such programs to change the free
|
||||||
case, there is little to gain by limiting the free library to free
|
libraries that are incorporated in them. (We have not seen how to achieve
|
||||||
software only, so we use the Lesser General Public License.
|
this as regards changes in header files, but we have achieved it as regards
|
||||||
|
changes in the actual functions of the Library.) The hope is that this
|
||||||
In other cases, permission to use a particular library in non-free
|
will lead to faster development of free libraries.
|
||||||
programs enables a greater number of people to use a large body of
|
|
||||||
free software. For example, permission to use the GNU C Library in
|
|
||||||
non-free programs enables many more people to use the whole GNU
|
|
||||||
operating system, as well as its variant, the GNU/Linux operating
|
|
||||||
system.
|
|
||||||
|
|
||||||
Although the Lesser General Public License is Less protective of the
|
|
||||||
users' freedom, it does ensure that the user of a program that is
|
|
||||||
linked with the Library has the freedom and the wherewithal to run
|
|
||||||
that program using a modified version of the Library.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
The precise terms and conditions for copying, distribution and
|
||||||
modification follow. Pay close attention to the difference between a
|
modification follow. Pay close attention to the difference between a
|
||||||
"work based on the library" and a "work that uses the library". The
|
"work based on the library" and a "work that uses the library". The
|
||||||
former contains code derived from the library, whereas the latter must
|
former contains code derived from the library, while the latter only
|
||||||
be combined with the library in order to run.
|
works together with the library.
|
||||||
|
|
||||||
|
Note that it is possible for a library to be covered by the ordinary
|
||||||
|
General Public License rather than by this special one.
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
0. This License Agreement applies to any software library or other
|
0. This License Agreement applies to any software library which
|
||||||
program which contains a notice placed by the copyright holder or
|
contains a notice placed by the copyright holder or other authorized
|
||||||
other authorized party saying it may be distributed under the terms of
|
party saying it may be distributed under the terms of this Library
|
||||||
this Lesser General Public License (also called "this License").
|
General Public License (also called "this License"). Each licensee is
|
||||||
Each licensee is addressed as "you".
|
addressed as "you".
|
||||||
|
|
||||||
A "library" means a collection of software functions and/or data
|
A "library" means a collection of software functions and/or data
|
||||||
prepared so as to be conveniently linked with application programs
|
prepared so as to be conveniently linked with application programs
|
||||||
@@ -268,7 +256,7 @@ distribute the object code for the work under the terms of Section 6.
|
|||||||
Any executables containing that work also fall under Section 6,
|
Any executables containing that work also fall under Section 6,
|
||||||
whether or not they are linked directly with the Library itself.
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
6. As an exception to the Sections above, you may also combine or
|
6. As an exception to the Sections above, you may also compile or
|
||||||
link a "work that uses the Library" with the Library to produce a
|
link a "work that uses the Library" with the Library to produce a
|
||||||
work containing portions of the Library, and distribute that work
|
work containing portions of the Library, and distribute that work
|
||||||
under terms of your choice, provided that the terms permit
|
under terms of your choice, provided that the terms permit
|
||||||
@@ -295,31 +283,23 @@ of these things:
|
|||||||
Library will not necessarily be able to recompile the application
|
Library will not necessarily be able to recompile the application
|
||||||
to use the modified definitions.)
|
to use the modified definitions.)
|
||||||
|
|
||||||
b) Use a suitable shared library mechanism for linking with the
|
b) Accompany the work with a written offer, valid for at
|
||||||
Library. A suitable mechanism is one that (1) uses at run time a
|
|
||||||
copy of the library already present on the user's computer system,
|
|
||||||
rather than copying library functions into the executable, and (2)
|
|
||||||
will operate properly with a modified version of the library, if
|
|
||||||
the user installs one, as long as the modified version is
|
|
||||||
interface-compatible with the version that the work was made with.
|
|
||||||
|
|
||||||
c) Accompany the work with a written offer, valid for at
|
|
||||||
least three years, to give the same user the materials
|
least three years, to give the same user the materials
|
||||||
specified in Subsection 6a, above, for a charge no more
|
specified in Subsection 6a, above, for a charge no more
|
||||||
than the cost of performing this distribution.
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
d) If distribution of the work is made by offering access to copy
|
c) If distribution of the work is made by offering access to copy
|
||||||
from a designated place, offer equivalent access to copy the above
|
from a designated place, offer equivalent access to copy the above
|
||||||
specified materials from the same place.
|
specified materials from the same place.
|
||||||
|
|
||||||
e) Verify that the user has already received a copy of these
|
d) Verify that the user has already received a copy of these
|
||||||
materials or that you have already sent this user a copy.
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
For an executable, the required form of the "work that uses the
|
For an executable, the required form of the "work that uses the
|
||||||
Library" must include any data and utility programs needed for
|
Library" must include any data and utility programs needed for
|
||||||
reproducing the executable from it. However, as a special exception,
|
reproducing the executable from it. However, as a special exception,
|
||||||
the materials to be distributed need not include anything that is
|
the source code distributed need not include anything that is normally
|
||||||
normally distributed (in either source or binary form) with the major
|
distributed (in either source or binary form) with the major
|
||||||
components (compiler, kernel, and so on) of the operating system on
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
which the executable runs, unless that component itself accompanies
|
which the executable runs, unless that component itself accompanies
|
||||||
the executable.
|
the executable.
|
||||||
@@ -368,7 +348,7 @@ Library), the recipient automatically receives a license from the
|
|||||||
original licensor to copy, distribute, link with or modify the Library
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
subject to these terms and conditions. You may not impose any further
|
subject to these terms and conditions. You may not impose any further
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
You are not responsible for enforcing compliance by third parties with
|
You are not responsible for enforcing compliance by third parties to
|
||||||
this License.
|
this License.
|
||||||
|
|
||||||
11. If, as a consequence of a court judgment or allegation of patent
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
@@ -411,7 +391,7 @@ excluded. In such case, this License incorporates the limitation as if
|
|||||||
written in the body of this License.
|
written in the body of this License.
|
||||||
|
|
||||||
13. The Free Software Foundation may publish revised and/or new
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
versions of the Lesser General Public License from time to time.
|
versions of the Library General Public License from time to time.
|
||||||
Such new versions will be similar in spirit to the present version,
|
Such new versions will be similar in spirit to the present version,
|
||||||
but may differ in detail to address new problems or concerns.
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
@@ -457,7 +437,7 @@ DAMAGES.
|
|||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
How to Apply These Terms to Your New Libraries
|
Appendix: How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
If you develop a new library, and you want it to be of the greatest
|
If you develop a new library, and you want it to be of the greatest
|
||||||
possible use to the public, we recommend making it free software that
|
possible use to the public, we recommend making it free software that
|
||||||
@@ -474,18 +454,19 @@ convey the exclusion of warranty; and each file should have at least the
|
|||||||
Copyright (C) <year> <name of author>
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU Lesser General Public
|
modify it under the terms of the GNU Library General Public
|
||||||
License as published by the Free Software Foundation; either
|
License as published by the Free Software Foundation; either
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
This library is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
Lesser General Public License for more details.
|
Library General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
You should have received a copy of the GNU Library General Public
|
||||||
License along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||||
|
MA 02111-1307, USA
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
@@ -500,5 +481,3 @@ necessary. Here is a sample; alter the names:
|
|||||||
Ty Coon, President of Vice
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
That's all there is to it!
|
That's all there is to it!
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
34
INSTALL
34
INSTALL
@@ -1,30 +1,44 @@
|
|||||||
Installation
|
LVM2 installation
|
||||||
============
|
=================
|
||||||
|
|
||||||
1) Generate custom makefiles.
|
1) Install device-mapper
|
||||||
|
|
||||||
|
Ensure the device-mapper has been installed on the machine.
|
||||||
|
|
||||||
|
The device-mapper should be in the kernel (look for 'device-mapper'
|
||||||
|
messages in the kernel logs) and /usr/include/libdevmapper.h
|
||||||
|
and libdevmapper.so should be present.
|
||||||
|
|
||||||
|
The device-mapper is available from:
|
||||||
|
ftp://ftp.sistina.com/pub/LVM2/device-mapper/
|
||||||
|
|
||||||
|
|
||||||
|
2) Generate custom makefiles.
|
||||||
|
|
||||||
Run the 'configure' script from the top directory.
|
Run the 'configure' script from the top directory.
|
||||||
|
|
||||||
|
If you wish to use the built-in LVM2 shell and have GNU readline
|
||||||
|
installed (http://www.gnu.org/directory/readline.html) use:
|
||||||
|
./configure --enable-readline
|
||||||
|
|
||||||
If you don't want to include the LVM1 backwards-compatibility code use:
|
If you don't want to include the LVM1 backwards-compatibility code use:
|
||||||
./configure --with-lvm1=none
|
./configure --with-lvm1=none
|
||||||
|
|
||||||
To separate the LVM1 support into a shared library loaded by lvm.conf use:
|
To separate the LVM1 support into a shared library loaded by lvm.conf use:
|
||||||
./configure --with-lvm1=shared
|
./configure --with-lvm1=shared
|
||||||
|
|
||||||
Use ./configure --help to see other options.
|
|
||||||
|
|
||||||
2) Build and install.
|
3) Build and install LVM2.
|
||||||
|
|
||||||
Run 'make' from the top directory to build everything you configured.
|
Run 'make install' from the top directory.
|
||||||
Run 'make install' to build and install everything you configured.
|
|
||||||
|
|
||||||
If you only want the device-mapper libraries and tools use
|
|
||||||
'make device-mapper' or 'make install_device-mapper'.
|
|
||||||
|
|
||||||
3) If using LVM2, create a configuration file.
|
4) Create a configuration file
|
||||||
|
|
||||||
The tools will work fine without a configuration file being
|
The tools will work fine without a configuration file being
|
||||||
present, but you ought to review the example file in doc/example.conf.
|
present, but you ought to review the example file in doc/example.conf.
|
||||||
|
For example, specifying the devices that LVM2 is to use can
|
||||||
|
make the tools run more efficiently - and avoid scanning /dev/cdrom!
|
||||||
|
|
||||||
Please also refer to the WHATS_NEW file and the manual pages for the
|
Please also refer to the WHATS_NEW file and the manual pages for the
|
||||||
individual commands.
|
individual commands.
|
||||||
|
|||||||
249
Makefile.in
249
Makefile.in
@@ -1,246 +1,35 @@
|
|||||||
#
|
#
|
||||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
# Copyright (C) 2001 Sistina Software
|
||||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
#
|
||||||
# This file is part of LVM2.
|
# This LVM library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Library General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2 of the License, or (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
# This LVM library is distributed in the hope that it will be useful,
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# of the GNU General Public License v.2.
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Library General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU Library General Public
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
# License along with this LVM library; if not, write to the Free
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||||
|
# MA 02111-1307, USA
|
||||||
|
|
||||||
srcdir = @srcdir@
|
srcdir = @srcdir@
|
||||||
top_srcdir = @top_srcdir@
|
top_srcdir = @top_srcdir@
|
||||||
top_builddir = @top_builddir@
|
VPATH = @srcdir@
|
||||||
abs_top_builddir = @abs_top_builddir@
|
|
||||||
abs_top_srcdir = @abs_top_srcdir@
|
|
||||||
|
|
||||||
SUBDIRS = conf daemons include lib libdaemon libdm man scripts tools
|
SUBDIRS = include man lib tools
|
||||||
|
|
||||||
ifeq ("@UDEV_RULES@", "yes")
|
|
||||||
SUBDIRS += udev
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@INTL@", "yes")
|
|
||||||
SUBDIRS += po
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@APPLIB@", "yes")
|
|
||||||
SUBDIRS += liblvm
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@PYTHON_BINDINGS@", "yes")
|
|
||||||
SUBDIRS += python
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(MAKECMDGOALS),clean)
|
|
||||||
SUBDIRS += test
|
|
||||||
endif
|
|
||||||
# FIXME Should use intermediate Makefiles here!
|
|
||||||
ifeq ($(MAKECMDGOALS),distclean)
|
ifeq ($(MAKECMDGOALS),distclean)
|
||||||
SUBDIRS = conf include man test scripts \
|
SUBDIRS += lib/format1 \
|
||||||
libdaemon lib tools daemons libdm \
|
test/mm test/device test/format1 test/regex test/filters
|
||||||
udev po liblvm python \
|
|
||||||
unit-tests/datastruct unit-tests/mm unit-tests/regex
|
|
||||||
tools.distclean: test.distclean
|
|
||||||
endif
|
endif
|
||||||
DISTCLEAN_DIRS += lcov_reports*
|
|
||||||
DISTCLEAN_TARGETS += config.cache config.log config.status make.tmpl
|
|
||||||
|
|
||||||
include make.tmpl
|
include make.tmpl
|
||||||
|
|
||||||
libdm: include
|
lib: include
|
||||||
libdaemon: include
|
tools: include lib
|
||||||
lib: libdm libdaemon
|
|
||||||
liblvm: lib
|
|
||||||
daemons: lib libdaemon tools
|
|
||||||
tools: lib libdaemon device-mapper
|
|
||||||
po: tools daemons
|
|
||||||
man: tools
|
|
||||||
all_man: tools
|
|
||||||
scripts: liblvm libdm
|
|
||||||
|
|
||||||
lib.device-mapper: include.device-mapper
|
|
||||||
libdm.device-mapper: include.device-mapper
|
|
||||||
liblvm.device-mapper: include.device-mapper
|
|
||||||
daemons.device-mapper: libdm.device-mapper
|
|
||||||
tools.device-mapper: libdm.device-mapper
|
|
||||||
scripts.device-mapper: include.device-mapper
|
|
||||||
device-mapper: tools.device-mapper daemons.device-mapper man.device-mapper
|
|
||||||
|
|
||||||
ifeq ("@INTL@", "yes")
|
|
||||||
lib.pofile: include.pofile
|
|
||||||
tools.pofile: lib.pofile
|
|
||||||
daemons.pofile: lib.pofile
|
|
||||||
po.pofile: tools.pofile daemons.pofile
|
|
||||||
pofile: po.pofile
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@PYTHON_BINDINGS@", "yes")
|
|
||||||
python: liblvm
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ("$(CFLOW_CMD)", "")
|
|
||||||
tools.cflow: libdm.cflow lib.cflow
|
|
||||||
daemons.cflow: tools.cflow
|
|
||||||
cflow: include.cflow
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ("@CSCOPE_CMD@", "")
|
|
||||||
cscope.out:
|
|
||||||
@CSCOPE_CMD@ -b -R -s$(top_srcdir)
|
|
||||||
all: cscope.out
|
|
||||||
endif
|
|
||||||
DISTCLEAN_TARGETS += cscope.out
|
|
||||||
CLEAN_DIRS += autom4te.cache
|
|
||||||
|
|
||||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit: all
|
|
||||||
$(MAKE) -C test $(@)
|
|
||||||
|
|
||||||
conf.generate man.generate: tools
|
|
||||||
|
|
||||||
# how to use parenthesis in makefiles
|
|
||||||
leftparen:=(
|
|
||||||
LVM_VER := $(firstword $(subst $(leftparen), ,$(LVM_VERSION)))
|
|
||||||
VER := LVM2.$(LVM_VER)
|
|
||||||
# release file name
|
|
||||||
FILE_VER := $(VER).tgz
|
|
||||||
CLEAN_TARGETS += $(FILE_VER)
|
|
||||||
CLEAN_DIRS += $(rpmbuilddir)
|
|
||||||
|
|
||||||
dist:
|
|
||||||
@echo "Generating $(FILE_VER)";\
|
|
||||||
(cd $(top_srcdir); git ls-tree -r HEAD --name-only | xargs tar --transform "s,^,$(VER)/," -c) | gzip >$(FILE_VER)
|
|
||||||
|
|
||||||
rpm: dist
|
|
||||||
$(RM) -r $(rpmbuilddir)/SOURCES
|
|
||||||
$(MKDIR_P) $(rpmbuilddir)/SOURCES
|
|
||||||
$(LN_S) -f $(abs_top_builddir)/$(FILE_VER) $(rpmbuilddir)/SOURCES
|
|
||||||
$(LN_S) -f $(abs_top_srcdir)/spec/build.inc $(rpmbuilddir)/SOURCES
|
|
||||||
$(LN_S) -f $(abs_top_srcdir)/spec/macros.inc $(rpmbuilddir)/SOURCES
|
|
||||||
$(LN_S) -f $(abs_top_srcdir)/spec/packages.inc $(rpmbuilddir)/SOURCES
|
|
||||||
DM_VER=$$(cut -d- -f1 $(top_srcdir)/VERSION_DM);\
|
|
||||||
GIT_VER=$$(cd $(top_srcdir); git describe | cut -d- --output-delimiter=. -f2,3 || echo 0);\
|
|
||||||
sed -e "s,\(device_mapper_version\) [0-9.]*$$,\1 $$DM_VER," \
|
|
||||||
-e "s,^\(Version:[^0-9%]*\)[0-9.]*$$,\1 $(LVM_VER)," \
|
|
||||||
-e "s,^\(Release:[^0-9%]*\)[0-9.]\+,\1 $$GIT_VER," \
|
|
||||||
$(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc
|
|
||||||
rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec
|
|
||||||
|
|
||||||
generate: conf.generate man.generate
|
|
||||||
$(MAKE) -C conf generate
|
|
||||||
$(MAKE) -C man generate
|
|
||||||
|
|
||||||
all_man:
|
|
||||||
$(MAKE) -C man all_man
|
|
||||||
|
|
||||||
install_system_dirs:
|
|
||||||
$(INSTALL_DIR) $(DESTDIR)$(DEFAULT_SYS_DIR)
|
|
||||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_ARCHIVE_DIR)
|
|
||||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_BACKUP_DIR)
|
|
||||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_CACHE_DIR)
|
|
||||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_LOCK_DIR)
|
|
||||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_RUN_DIR)
|
|
||||||
$(INSTALL_ROOT_DATA) /dev/null $(DESTDIR)$(DEFAULT_CACHE_DIR)/.cache
|
|
||||||
|
|
||||||
install_initscripts:
|
|
||||||
$(MAKE) -C scripts install_initscripts
|
|
||||||
|
|
||||||
install_systemd_generators:
|
|
||||||
$(MAKE) -C scripts install_systemd_generators
|
|
||||||
$(MAKE) -C man install_systemd_generators
|
|
||||||
|
|
||||||
install_systemd_units:
|
|
||||||
$(MAKE) -C scripts install_systemd_units
|
|
||||||
|
|
||||||
install_all_man:
|
|
||||||
$(MAKE) -C man install_all_man
|
|
||||||
|
|
||||||
ifeq ("@PYTHON_BINDINGS@", "yes")
|
|
||||||
install_python_bindings:
|
|
||||||
$(MAKE) -C liblvm/python install_python_bindings
|
|
||||||
endif
|
|
||||||
|
|
||||||
install_tmpfiles_configuration:
|
|
||||||
$(MAKE) -C scripts install_tmpfiles_configuration
|
|
||||||
|
|
||||||
LCOV_TRACES = libdm.info lib.info liblvm.info tools.info \
|
|
||||||
libdaemon/client.info libdaemon/server.info \
|
|
||||||
daemons/clvmd.info \
|
|
||||||
daemons/dmeventd.info \
|
|
||||||
daemons/lvmetad.info \
|
|
||||||
daemons/lvmlockd.info \
|
|
||||||
daemons/lvmpolld.info
|
|
||||||
|
|
||||||
CLEAN_TARGETS += $(LCOV_TRACES)
|
|
||||||
|
|
||||||
ifneq ("$(LCOV)", "")
|
|
||||||
.PHONY: lcov-reset lcov lcov-dated $(LCOV_TRACES)
|
|
||||||
|
|
||||||
ifeq ($(MAKECMDGOALS),lcov-dated)
|
|
||||||
LCOV_REPORTS_DIR := lcov_reports-$(shell date +%Y%m%d%k%M%S)
|
|
||||||
lcov-dated: lcov
|
|
||||||
else
|
|
||||||
LCOV_REPORTS_DIR := lcov_reports
|
|
||||||
endif
|
|
||||||
|
|
||||||
lcov-reset:
|
|
||||||
$(LCOV) --zerocounters $(addprefix -d , $(basename $(LCOV_TRACES)))
|
|
||||||
|
|
||||||
# maybe use subdirs processing to create tracefiles...
|
|
||||||
$(LCOV_TRACES):
|
|
||||||
$(LCOV) -b $(basename $@) -d $(basename $@) \
|
|
||||||
--ignore-errors source -c -o - | $(SED) \
|
|
||||||
-e "s/\(dmeventd_lvm.[ch]\)/plugins\/lvm2\/\1/" \
|
|
||||||
-e "s/dmeventd_\(mirror\|snapshot\|thin\|raid\)\.c/plugins\/\1\/dmeventd_\1\.c/" \
|
|
||||||
>$@
|
|
||||||
|
|
||||||
ifneq ("$(GENHTML)", "")
|
|
||||||
lcov: $(LCOV_TRACES)
|
|
||||||
$(RM) -r $(LCOV_REPORTS_DIR)
|
|
||||||
$(MKDIR_P) $(LCOV_REPORTS_DIR)
|
|
||||||
for i in $(LCOV_TRACES); do \
|
|
||||||
test -s $$i -a $$(wc -w <$$i) -ge 100 && lc="$$lc $$i"; \
|
|
||||||
done; \
|
|
||||||
test -z "$$lc" || $(GENHTML) -p @abs_top_builddir@ \
|
|
||||||
-o $(LCOV_REPORTS_DIR) $$lc
|
|
||||||
endif
|
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("$(TESTING)", "yes")
|
|
||||||
# testing and report generation
|
|
||||||
RUBY=ruby1.9 -Ireport-generators/lib -Ireport-generators/test
|
|
||||||
|
|
||||||
.PHONY: unit-test ruby-test test-programs
|
|
||||||
|
|
||||||
# FIXME: put dependencies on libdm and liblvm
|
|
||||||
# FIXME: Should be handled by Makefiles in subdirs, not here at top level.
|
|
||||||
test-programs:
|
|
||||||
cd unit-tests/regex && $(MAKE)
|
|
||||||
cd unit-tests/datastruct && $(MAKE)
|
|
||||||
cd unit-tests/mm && $(MAKE)
|
|
||||||
|
|
||||||
unit-test: test-programs
|
|
||||||
$(RUBY) report-generators/unit_test.rb $(shell find . -name TESTS)
|
|
||||||
$(RUBY) report-generators/title_page.rb
|
|
||||||
|
|
||||||
memcheck: test-programs
|
|
||||||
$(RUBY) report-generators/memcheck.rb $(shell find . -name TESTS)
|
|
||||||
$(RUBY) report-generators/title_page.rb
|
|
||||||
|
|
||||||
ruby-test:
|
|
||||||
$(RUBY) report-generators/test/ts.rb
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(shell which ctags),)
|
|
||||||
.PHONY: tags
|
|
||||||
tags:
|
|
||||||
test -z "$(shell find $(top_srcdir) -type f -name '*.[ch]' -newer tags 2>/dev/null | head -1)" || $(RM) tags
|
|
||||||
test -f tags || find $(top_srcdir) -maxdepth 5 -type f -name '*.[ch]' -exec ctags -a '{}' +
|
|
||||||
|
|
||||||
CLEAN_TARGETS += tags
|
|
||||||
endif
|
|
||||||
|
|||||||
41
README
41
README
@@ -1,34 +1,25 @@
|
|||||||
This tree contains the LVM2 and device-mapper tools and libraries.
|
This directory contains a beta release of LVM2, the new version of
|
||||||
|
the userland LVM tools designed for the new device-mapper for
|
||||||
|
the Linux kernel.
|
||||||
|
|
||||||
For more information about LVM2 read the changelog in the WHATS_NEW file.
|
The device-mapper needs to be installed before compiling these LVM2 tools.
|
||||||
|
|
||||||
|
For more information about LVM2 read the WHATS_NEW file.
|
||||||
Installation instructions are in INSTALL.
|
Installation instructions are in INSTALL.
|
||||||
|
|
||||||
|
This is beta-quality software, released for testing purposes only.
|
||||||
There is no warranty - see COPYING and COPYING.LIB.
|
There is no warranty - see COPYING and COPYING.LIB.
|
||||||
|
|
||||||
Tarballs are available from:
|
Tarballs are available from:
|
||||||
ftp://sourceware.org/pub/lvm2/
|
ftp://ftp.sistina.com/pub/LVM2/tools/
|
||||||
ftp://sources.redhat.com/pub/lvm2/
|
ftp://ftp.sistina.com/pub/LVM2/device-mapper/
|
||||||
|
|
||||||
The source code is stored in git:
|
To access the CVS tree use:
|
||||||
https://sourceware.org/git/?p=lvm2.git
|
cvs -d :pserver:cvs@tech.sistina.com:/data/cvs login
|
||||||
git clone git://sourceware.org/git/lvm2.git
|
CVS password: cvs1
|
||||||
|
cvs -d :pserver:cvs@tech.sistina.com:/data/cvs checkout LVM2
|
||||||
|
|
||||||
Mailing list for general discussion related to LVM2:
|
Mailing list for discussion/bug reports etc.
|
||||||
linux-lvm@redhat.com
|
lvm-devel@sistina.com
|
||||||
Subscribe from https://www.redhat.com/mailman/listinfo/linux-lvm
|
Subscribe from http://lists.sistina.com/mailman/listinfo/lvm-devel
|
||||||
|
|
||||||
Mailing lists for LVM2 development, patches and commits:
|
|
||||||
lvm-devel@redhat.com
|
|
||||||
Subscribe from https://www.redhat.com/mailman/listinfo/lvm-devel
|
|
||||||
|
|
||||||
lvm2-commits@lists.fedorahosted.org (Read-only archive of commits)
|
|
||||||
Subscribe from https://fedorahosted.org/mailman/listinfo/lvm2-commits
|
|
||||||
|
|
||||||
Mailing list for device-mapper development, including kernel patches
|
|
||||||
and multipath-tools:
|
|
||||||
dm-devel@redhat.com
|
|
||||||
Subscribe from https://www.redhat.com/mailman/listinfo/dm-devel
|
|
||||||
|
|
||||||
The source code repository used until 7th June 2012 is accessible here:
|
|
||||||
http://sources.redhat.com/cgi-bin/cvsweb.cgi/LVM2/?cvsroot=lvm2.
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
1.02.143-git (2017-07-20)
|
|
||||||
1378
WHATS_NEW_DM
1378
WHATS_NEW_DM
File diff suppressed because it is too large
Load Diff
234
acinclude.m4
234
acinclude.m4
@@ -1,234 +0,0 @@
|
|||||||
dnl AC_GCC_VERSION
|
|
||||||
dnl check for compiler version
|
|
||||||
dnl sets COMPILER_VERSION and GCC_VERSION
|
|
||||||
|
|
||||||
AC_DEFUN([AC_CC_VERSION],
|
|
||||||
[
|
|
||||||
AC_MSG_CHECKING([C compiler version])
|
|
||||||
COMPILER_VERSION=`$CC -v 2>&1 | grep version`
|
|
||||||
case "$COMPILER_VERSION" in
|
|
||||||
*gcc*)
|
|
||||||
dnl Ok, how to turn $3 into the real $3
|
|
||||||
GCC_VERSION=`echo $COMPILER_VERSION | \
|
|
||||||
sed -e 's/[[^ ]]*\ [[^ ]]*\ \([[^ ]]*\)\ .*/\1/'` ;;
|
|
||||||
*) GCC_VERSION=unknown ;;
|
|
||||||
esac
|
|
||||||
AC_MSG_RESULT($GCC_VERSION)
|
|
||||||
])
|
|
||||||
|
|
||||||
dnl AC_TRY_CCFLAG([CCFLAG], [VAR], [ACTION-IF-WORKS], [ACTION-IF-FAILS])
|
|
||||||
dnl check if $CC supports a given flag
|
|
||||||
|
|
||||||
AC_DEFUN([AC_TRY_CCFLAG],
|
|
||||||
[
|
|
||||||
AC_REQUIRE([AC_PROG_CC])
|
|
||||||
ac_save_CFLAGS=$CFLAGS
|
|
||||||
CFLAGS=$1
|
|
||||||
AC_CACHE_CHECK([whether $CC accepts $1 flag], [ac_cv_flag_$2],
|
|
||||||
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
|
|
||||||
[AS_VAR_SET([ac_cv_flag_$2], [yes])],
|
|
||||||
[AS_VAR_SET([ac_cv_flag_$2], [no])])])
|
|
||||||
CFLAGS=$ac_save_CFLAGS
|
|
||||||
$2=AS_VAR_GET([ac_cv_flag_$2])
|
|
||||||
if test "$2" = yes; then
|
|
||||||
ifelse([$3], [], [:], [$3])
|
|
||||||
else
|
|
||||||
ifelse([$4], [], [:], [$4])
|
|
||||||
fi
|
|
||||||
])
|
|
||||||
|
|
||||||
dnl AC_IF_YES([TEST-FOR-YES], [ACTION-IF-TRUE], [ACTION-IF-FALSE])
|
|
||||||
dnl AS_IF() abstraction, checks shell variable for 'yes'
|
|
||||||
AC_DEFUN([AC_IF_YES], [AS_IF([test $$1 = yes], [$2], [$3])])
|
|
||||||
|
|
||||||
dnl AC_TRY_LDFLAGS([LDFLAGS], [VAR], [ACTION-IF-WORKS], [ACTION-IF-FAILS])
|
|
||||||
dnl check if $CC supports given ld flags
|
|
||||||
|
|
||||||
AC_DEFUN([AC_TRY_LDFLAGS],
|
|
||||||
[
|
|
||||||
AC_REQUIRE([AC_PROG_CC])
|
|
||||||
ac_save_LDFLAGS=$LDFLAGS
|
|
||||||
LDFLAGS=$1
|
|
||||||
AC_CACHE_CHECK([whether $CC accepts $1 ld flags], [ac_cv_flag_$2],
|
|
||||||
[AC_LINK_IFELSE([AC_LANG_PROGRAM()],
|
|
||||||
[AS_VAR_SET([ac_cv_flag_$2], [yes])],
|
|
||||||
[AS_VAR_SET([ac_cv_flag_$2], [no])])])
|
|
||||||
LDFLAGS=$ac_save_LDFLAGS
|
|
||||||
$2=AS_VAR_GET([ac_cv_flag_$2])
|
|
||||||
if test "$2" = yes; then
|
|
||||||
ifelse([$3], [], [:], [$3])
|
|
||||||
else
|
|
||||||
ifelse([$4], [], [:], [$4])
|
|
||||||
fi
|
|
||||||
])
|
|
||||||
|
|
||||||
# ===========================================================================
|
|
||||||
# http://www.gnu.org/software/autoconf-archive/ax_gcc_builtin.html
|
|
||||||
# ===========================================================================
|
|
||||||
#
|
|
||||||
# SYNOPSIS
|
|
||||||
#
|
|
||||||
# AX_GCC_BUILTIN(BUILTIN)
|
|
||||||
#
|
|
||||||
# DESCRIPTION
|
|
||||||
#
|
|
||||||
# This macro checks if the compiler supports one of GCC's built-in
|
|
||||||
# functions; many other compilers also provide those same built-ins.
|
|
||||||
#
|
|
||||||
# The BUILTIN parameter is the name of the built-in function.
|
|
||||||
#
|
|
||||||
# If BUILTIN is supported define HAVE_<BUILTIN>. Keep in mind that since
|
|
||||||
# builtins usually start with two underscores they will be copied over
|
|
||||||
# into the HAVE_<BUILTIN> definition (e.g. HAVE___BUILTIN_EXPECT for
|
|
||||||
# __builtin_expect()).
|
|
||||||
#
|
|
||||||
# The macro caches its result in the ax_cv_have_<BUILTIN> variable (e.g.
|
|
||||||
# ax_cv_have___builtin_expect).
|
|
||||||
#
|
|
||||||
# The macro currently supports the following built-in functions:
|
|
||||||
#
|
|
||||||
# __builtin_assume_aligned
|
|
||||||
# __builtin_bswap16
|
|
||||||
# __builtin_bswap32
|
|
||||||
# __builtin_bswap64
|
|
||||||
# __builtin_choose_expr
|
|
||||||
# __builtin___clear_cache
|
|
||||||
# __builtin_clrsb
|
|
||||||
# __builtin_clrsbl
|
|
||||||
# __builtin_clrsbll
|
|
||||||
# __builtin_clz
|
|
||||||
# __builtin_clzl
|
|
||||||
# __builtin_clzll
|
|
||||||
# __builtin_complex
|
|
||||||
# __builtin_constant_p
|
|
||||||
# __builtin_ctz
|
|
||||||
# __builtin_ctzl
|
|
||||||
# __builtin_ctzll
|
|
||||||
# __builtin_expect
|
|
||||||
# __builtin_ffs
|
|
||||||
# __builtin_ffsl
|
|
||||||
# __builtin_ffsll
|
|
||||||
# __builtin_fpclassify
|
|
||||||
# __builtin_huge_val
|
|
||||||
# __builtin_huge_valf
|
|
||||||
# __builtin_huge_vall
|
|
||||||
# __builtin_inf
|
|
||||||
# __builtin_infd128
|
|
||||||
# __builtin_infd32
|
|
||||||
# __builtin_infd64
|
|
||||||
# __builtin_inff
|
|
||||||
# __builtin_infl
|
|
||||||
# __builtin_isinf_sign
|
|
||||||
# __builtin_nan
|
|
||||||
# __builtin_nand128
|
|
||||||
# __builtin_nand32
|
|
||||||
# __builtin_nand64
|
|
||||||
# __builtin_nanf
|
|
||||||
# __builtin_nanl
|
|
||||||
# __builtin_nans
|
|
||||||
# __builtin_nansf
|
|
||||||
# __builtin_nansl
|
|
||||||
# __builtin_object_size
|
|
||||||
# __builtin_parity
|
|
||||||
# __builtin_parityl
|
|
||||||
# __builtin_parityll
|
|
||||||
# __builtin_popcount
|
|
||||||
# __builtin_popcountl
|
|
||||||
# __builtin_popcountll
|
|
||||||
# __builtin_powi
|
|
||||||
# __builtin_powif
|
|
||||||
# __builtin_powil
|
|
||||||
# __builtin_prefetch
|
|
||||||
# __builtin_trap
|
|
||||||
# __builtin_types_compatible_p
|
|
||||||
# __builtin_unreachable
|
|
||||||
#
|
|
||||||
# Unsuppored built-ins will be tested with an empty parameter set and the
|
|
||||||
# result of the check might be wrong or meaningless so use with care.
|
|
||||||
#
|
|
||||||
# LICENSE
|
|
||||||
#
|
|
||||||
# Copyright (c) 2013 Gabriele Svelto <gabriele.svelto@gmail.com>
|
|
||||||
#
|
|
||||||
# Copying and distribution of this file, with or without modification, are
|
|
||||||
# permitted in any medium without royalty provided the copyright notice
|
|
||||||
# and this notice are preserved. This file is offered as-is, without any
|
|
||||||
# warranty.
|
|
||||||
|
|
||||||
#serial 3
|
|
||||||
|
|
||||||
AC_DEFUN([AX_GCC_BUILTIN], [
|
|
||||||
AS_VAR_PUSHDEF([ac_var], [ax_cv_have_$1])
|
|
||||||
|
|
||||||
AC_CACHE_CHECK([for $1], [ac_var], [
|
|
||||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [
|
|
||||||
m4_case([$1],
|
|
||||||
[__builtin_assume_aligned], [$1("", 0)],
|
|
||||||
[__builtin_bswap16], [$1(0)],
|
|
||||||
[__builtin_bswap32], [$1(0)],
|
|
||||||
[__builtin_bswap64], [$1(0)],
|
|
||||||
[__builtin_choose_expr], [$1(0, 0, 0)],
|
|
||||||
[__builtin___clear_cache], [$1("", "")],
|
|
||||||
[__builtin_clrsb], [$1(0)],
|
|
||||||
[__builtin_clrsbl], [$1(0)],
|
|
||||||
[__builtin_clrsbll], [$1(0)],
|
|
||||||
[__builtin_clz], [$1(0)],
|
|
||||||
[__builtin_clzl], [$1(0)],
|
|
||||||
[__builtin_clzll], [$1(0)],
|
|
||||||
[__builtin_complex], [$1(0.0, 0.0)],
|
|
||||||
[__builtin_constant_p], [$1(0)],
|
|
||||||
[__builtin_ctz], [$1(0)],
|
|
||||||
[__builtin_ctzl], [$1(0)],
|
|
||||||
[__builtin_ctzll], [$1(0)],
|
|
||||||
[__builtin_expect], [$1(0, 0)],
|
|
||||||
[__builtin_ffs], [$1(0)],
|
|
||||||
[__builtin_ffsl], [$1(0)],
|
|
||||||
[__builtin_ffsll], [$1(0)],
|
|
||||||
[__builtin_fpclassify], [$1(0, 1, 2, 3, 4, 0.0)],
|
|
||||||
[__builtin_huge_val], [$1()],
|
|
||||||
[__builtin_huge_valf], [$1()],
|
|
||||||
[__builtin_huge_vall], [$1()],
|
|
||||||
[__builtin_inf], [$1()],
|
|
||||||
[__builtin_infd128], [$1()],
|
|
||||||
[__builtin_infd32], [$1()],
|
|
||||||
[__builtin_infd64], [$1()],
|
|
||||||
[__builtin_inff], [$1()],
|
|
||||||
[__builtin_infl], [$1()],
|
|
||||||
[__builtin_isinf_sign], [$1(0.0)],
|
|
||||||
[__builtin_nan], [$1("")],
|
|
||||||
[__builtin_nand128], [$1("")],
|
|
||||||
[__builtin_nand32], [$1("")],
|
|
||||||
[__builtin_nand64], [$1("")],
|
|
||||||
[__builtin_nanf], [$1("")],
|
|
||||||
[__builtin_nanl], [$1("")],
|
|
||||||
[__builtin_nans], [$1("")],
|
|
||||||
[__builtin_nansf], [$1("")],
|
|
||||||
[__builtin_nansl], [$1("")],
|
|
||||||
[__builtin_object_size], [$1("", 0)],
|
|
||||||
[__builtin_parity], [$1(0)],
|
|
||||||
[__builtin_parityl], [$1(0)],
|
|
||||||
[__builtin_parityll], [$1(0)],
|
|
||||||
[__builtin_popcount], [$1(0)],
|
|
||||||
[__builtin_popcountl], [$1(0)],
|
|
||||||
[__builtin_popcountll], [$1(0)],
|
|
||||||
[__builtin_powi], [$1(0, 0)],
|
|
||||||
[__builtin_powif], [$1(0, 0)],
|
|
||||||
[__builtin_powil], [$1(0, 0)],
|
|
||||||
[__builtin_prefetch], [$1("")],
|
|
||||||
[__builtin_trap], [$1()],
|
|
||||||
[__builtin_types_compatible_p], [$1(int, int)],
|
|
||||||
[__builtin_unreachable], [$1()],
|
|
||||||
[m4_warn([syntax], [Unsupported built-in $1, the test may fail])
|
|
||||||
$1()]
|
|
||||||
)
|
|
||||||
])],
|
|
||||||
[AS_VAR_SET([ac_var], [yes])],
|
|
||||||
[AS_VAR_SET([ac_var], [no])])
|
|
||||||
])
|
|
||||||
|
|
||||||
AS_IF([test yes = AS_VAR_GET([ac_var])],
|
|
||||||
[AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1,
|
|
||||||
[Define to 1 if the system has the `$1' built-in function])], [])
|
|
||||||
|
|
||||||
AS_VAR_POPDEF([ac_var])
|
|
||||||
])
|
|
||||||
540
aclocal.m4
vendored
540
aclocal.m4
vendored
@@ -1,540 +0,0 @@
|
|||||||
# generated automatically by aclocal 1.15 -*- Autoconf -*-
|
|
||||||
|
|
||||||
# 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,
|
|
||||||
# with or without modifications, as long as this notice is preserved.
|
|
||||||
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
|
||||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
|
|
||||||
# ===========================================================================
|
|
||||||
# http://www.gnu.org/software/autoconf-archive/ax_python_module.html
|
|
||||||
# ===========================================================================
|
|
||||||
#
|
|
||||||
# SYNOPSIS
|
|
||||||
#
|
|
||||||
# AX_PYTHON_MODULE(modname[, fatal, python])
|
|
||||||
#
|
|
||||||
# DESCRIPTION
|
|
||||||
#
|
|
||||||
# Checks for Python module.
|
|
||||||
#
|
|
||||||
# If fatal is non-empty then absence of a module will trigger an error.
|
|
||||||
# The third parameter can either be "python" for Python 2 or "python3" for
|
|
||||||
# Python 3; defaults to Python 3.
|
|
||||||
#
|
|
||||||
# LICENSE
|
|
||||||
#
|
|
||||||
# Copyright (c) 2008 Andrew Collier
|
|
||||||
#
|
|
||||||
# Copying and distribution of this file, with or without modification, are
|
|
||||||
# permitted in any medium without royalty provided the copyright notice
|
|
||||||
# and this notice are preserved. This file is offered as-is, without any
|
|
||||||
# warranty.
|
|
||||||
|
|
||||||
#serial 8
|
|
||||||
|
|
||||||
AU_ALIAS([AC_PYTHON_MODULE], [AX_PYTHON_MODULE])
|
|
||||||
AC_DEFUN([AX_PYTHON_MODULE],[
|
|
||||||
if test -z $PYTHON;
|
|
||||||
then
|
|
||||||
if test -z "$3";
|
|
||||||
then
|
|
||||||
PYTHON="python3"
|
|
||||||
else
|
|
||||||
PYTHON="$3"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
PYTHON_NAME=`basename $PYTHON`
|
|
||||||
AC_MSG_CHECKING($PYTHON_NAME module: $1)
|
|
||||||
$PYTHON -c "import $1" 2>/dev/null
|
|
||||||
if test $? -eq 0;
|
|
||||||
then
|
|
||||||
AC_MSG_RESULT(yes)
|
|
||||||
eval AS_TR_CPP(HAVE_PYMOD_$1)=yes
|
|
||||||
else
|
|
||||||
AC_MSG_RESULT(no)
|
|
||||||
eval AS_TR_CPP(HAVE_PYMOD_$1)=no
|
|
||||||
#
|
|
||||||
if test -n "$2"
|
|
||||||
then
|
|
||||||
AC_MSG_ERROR(failed to find required module $1)
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
])
|
|
||||||
|
|
||||||
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
|
||||||
# serial 1 (pkg-config-0.24)
|
|
||||||
#
|
|
||||||
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
# General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
#
|
|
||||||
# As a special exception to the GNU General Public License, if you
|
|
||||||
# distribute this file as part of a program that contains a
|
|
||||||
# configuration script generated by Autoconf, you may include it under
|
|
||||||
# the same distribution terms that you use for the rest of that program.
|
|
||||||
|
|
||||||
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
|
||||||
# ----------------------------------
|
|
||||||
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
|
||||||
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
|
||||||
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
|
|
||||||
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
|
|
||||||
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
|
|
||||||
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
|
|
||||||
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
|
|
||||||
|
|
||||||
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
|
|
||||||
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
|
|
||||||
fi
|
|
||||||
if test -n "$PKG_CONFIG"; then
|
|
||||||
_pkg_min_version=m4_default([$1], [0.9.0])
|
|
||||||
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
|
|
||||||
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
|
|
||||||
AC_MSG_RESULT([yes])
|
|
||||||
else
|
|
||||||
AC_MSG_RESULT([no])
|
|
||||||
PKG_CONFIG=""
|
|
||||||
fi
|
|
||||||
fi[]dnl
|
|
||||||
])# PKG_PROG_PKG_CONFIG
|
|
||||||
|
|
||||||
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
|
||||||
#
|
|
||||||
# Check to see whether a particular set of modules exists. Similar
|
|
||||||
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
|
|
||||||
#
|
|
||||||
# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
|
||||||
# only at the first occurence in configure.ac, so if the first place
|
|
||||||
# it's called might be skipped (such as if it is within an "if", you
|
|
||||||
# have to call PKG_CHECK_EXISTS manually
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
AC_DEFUN([PKG_CHECK_EXISTS],
|
|
||||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
|
||||||
if test -n "$PKG_CONFIG" && \
|
|
||||||
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
|
|
||||||
m4_default([$2], [:])
|
|
||||||
m4_ifvaln([$3], [else
|
|
||||||
$3])dnl
|
|
||||||
fi])
|
|
||||||
|
|
||||||
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
|
||||||
# ---------------------------------------------
|
|
||||||
m4_define([_PKG_CONFIG],
|
|
||||||
[if test -n "$$1"; then
|
|
||||||
pkg_cv_[]$1="$$1"
|
|
||||||
elif test -n "$PKG_CONFIG"; then
|
|
||||||
PKG_CHECK_EXISTS([$3],
|
|
||||||
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
|
|
||||||
test "x$?" != "x0" && pkg_failed=yes ],
|
|
||||||
[pkg_failed=yes])
|
|
||||||
else
|
|
||||||
pkg_failed=untried
|
|
||||||
fi[]dnl
|
|
||||||
])# _PKG_CONFIG
|
|
||||||
|
|
||||||
# _PKG_SHORT_ERRORS_SUPPORTED
|
|
||||||
# -----------------------------
|
|
||||||
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
|
|
||||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
|
||||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
|
||||||
_pkg_short_errors_supported=yes
|
|
||||||
else
|
|
||||||
_pkg_short_errors_supported=no
|
|
||||||
fi[]dnl
|
|
||||||
])# _PKG_SHORT_ERRORS_SUPPORTED
|
|
||||||
|
|
||||||
|
|
||||||
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
|
||||||
# [ACTION-IF-NOT-FOUND])
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# Note that if there is a possibility the first call to
|
|
||||||
# PKG_CHECK_MODULES might not happen, you should be sure to include an
|
|
||||||
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
AC_DEFUN([PKG_CHECK_MODULES],
|
|
||||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
|
||||||
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
|
|
||||||
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
|
|
||||||
|
|
||||||
pkg_failed=no
|
|
||||||
AC_MSG_CHECKING([for $1])
|
|
||||||
|
|
||||||
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
|
|
||||||
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
|
|
||||||
|
|
||||||
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
|
|
||||||
and $1[]_LIBS to avoid the need to call pkg-config.
|
|
||||||
See the pkg-config man page for more details.])
|
|
||||||
|
|
||||||
if test $pkg_failed = yes; then
|
|
||||||
AC_MSG_RESULT([no])
|
|
||||||
_PKG_SHORT_ERRORS_SUPPORTED
|
|
||||||
if test $_pkg_short_errors_supported = yes; then
|
|
||||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
|
|
||||||
else
|
|
||||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
|
|
||||||
fi
|
|
||||||
# Put the nasty error message in config.log where it belongs
|
|
||||||
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
|
|
||||||
|
|
||||||
m4_default([$4], [AC_MSG_ERROR(
|
|
||||||
[Package requirements ($2) were not met:
|
|
||||||
|
|
||||||
$$1_PKG_ERRORS
|
|
||||||
|
|
||||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
|
||||||
installed software in a non-standard prefix.
|
|
||||||
|
|
||||||
_PKG_TEXT])[]dnl
|
|
||||||
])
|
|
||||||
elif test $pkg_failed = untried; then
|
|
||||||
AC_MSG_RESULT([no])
|
|
||||||
m4_default([$4], [AC_MSG_FAILURE(
|
|
||||||
[The pkg-config script could not be found or is too old. Make sure it
|
|
||||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
|
||||||
path to pkg-config.
|
|
||||||
|
|
||||||
_PKG_TEXT
|
|
||||||
|
|
||||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
|
|
||||||
])
|
|
||||||
else
|
|
||||||
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
|
|
||||||
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
|
|
||||||
AC_MSG_RESULT([yes])
|
|
||||||
$3
|
|
||||||
fi[]dnl
|
|
||||||
])# PKG_CHECK_MODULES
|
|
||||||
|
|
||||||
|
|
||||||
# PKG_INSTALLDIR(DIRECTORY)
|
|
||||||
# -------------------------
|
|
||||||
# Substitutes the variable pkgconfigdir as the location where a module
|
|
||||||
# should install pkg-config .pc files. By default the directory is
|
|
||||||
# $libdir/pkgconfig, but the default can be changed by passing
|
|
||||||
# DIRECTORY. The user can override through the --with-pkgconfigdir
|
|
||||||
# parameter.
|
|
||||||
AC_DEFUN([PKG_INSTALLDIR],
|
|
||||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
|
|
||||||
m4_pushdef([pkg_description],
|
|
||||||
[pkg-config installation directory @<:@]pkg_default[@:>@])
|
|
||||||
AC_ARG_WITH([pkgconfigdir],
|
|
||||||
[AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
|
|
||||||
[with_pkgconfigdir=]pkg_default)
|
|
||||||
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
|
|
||||||
m4_popdef([pkg_default])
|
|
||||||
m4_popdef([pkg_description])
|
|
||||||
]) dnl PKG_INSTALLDIR
|
|
||||||
|
|
||||||
|
|
||||||
# PKG_NOARCH_INSTALLDIR(DIRECTORY)
|
|
||||||
# -------------------------
|
|
||||||
# Substitutes the variable noarch_pkgconfigdir as the location where a
|
|
||||||
# module should install arch-independent pkg-config .pc files. By
|
|
||||||
# default the directory is $datadir/pkgconfig, but the default can be
|
|
||||||
# changed by passing DIRECTORY. The user can override through the
|
|
||||||
# --with-noarch-pkgconfigdir parameter.
|
|
||||||
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
|
|
||||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
|
|
||||||
m4_pushdef([pkg_description],
|
|
||||||
[pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
|
|
||||||
AC_ARG_WITH([noarch-pkgconfigdir],
|
|
||||||
[AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
|
|
||||||
[with_noarch_pkgconfigdir=]pkg_default)
|
|
||||||
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
|
|
||||||
m4_popdef([pkg_default])
|
|
||||||
m4_popdef([pkg_description])
|
|
||||||
]) dnl PKG_NOARCH_INSTALLDIR
|
|
||||||
|
|
||||||
|
|
||||||
# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
|
||||||
# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
|
||||||
# -------------------------------------------
|
|
||||||
# Retrieves the value of the pkg-config variable for the given module.
|
|
||||||
AC_DEFUN([PKG_CHECK_VAR],
|
|
||||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
|
||||||
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
|
|
||||||
|
|
||||||
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
|
|
||||||
AS_VAR_COPY([$1], [pkg_cv_][$1])
|
|
||||||
|
|
||||||
AS_VAR_IF([$1], [""], [$5], [$4])dnl
|
|
||||||
])# PKG_CHECK_VAR
|
|
||||||
|
|
||||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
|
||||||
#
|
|
||||||
# This file is free software; the Free Software Foundation
|
|
||||||
# gives unlimited permission to copy and/or distribute it,
|
|
||||||
# with or without modifications, as long as this notice is preserved.
|
|
||||||
|
|
||||||
|
|
||||||
# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Adds support for distributing Python modules and packages. To
|
|
||||||
# install modules, copy them to $(pythondir), using the python_PYTHON
|
|
||||||
# automake variable. To install a package with the same name as the
|
|
||||||
# automake package, install to $(pkgpythondir), or use the
|
|
||||||
# pkgpython_PYTHON automake variable.
|
|
||||||
#
|
|
||||||
# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as
|
|
||||||
# locations to install python extension modules (shared libraries).
|
|
||||||
# Another macro is required to find the appropriate flags to compile
|
|
||||||
# extension modules.
|
|
||||||
#
|
|
||||||
# If your package is configured with a different prefix to python,
|
|
||||||
# users will have to add the install directory to the PYTHONPATH
|
|
||||||
# environment variable, or create a .pth file (see the python
|
|
||||||
# documentation for details).
|
|
||||||
#
|
|
||||||
# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will
|
|
||||||
# cause an error if the version of python installed on the system
|
|
||||||
# doesn't meet the requirement. MINIMUM-VERSION should consist of
|
|
||||||
# numbers and dots only.
|
|
||||||
AC_DEFUN([AM_PATH_PYTHON],
|
|
||||||
[
|
|
||||||
dnl Find a Python interpreter. Python versions prior to 2.0 are not
|
|
||||||
dnl supported. (2.0 was released on October 16, 2000).
|
|
||||||
m4_define_default([_AM_PYTHON_INTERPRETER_LIST],
|
|
||||||
[python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl
|
|
||||||
python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0])
|
|
||||||
|
|
||||||
AC_ARG_VAR([PYTHON], [the Python interpreter])
|
|
||||||
|
|
||||||
m4_if([$1],[],[
|
|
||||||
dnl No version check is needed.
|
|
||||||
# Find any Python interpreter.
|
|
||||||
if test -z "$PYTHON"; then
|
|
||||||
AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :)
|
|
||||||
fi
|
|
||||||
am_display_PYTHON=python
|
|
||||||
], [
|
|
||||||
dnl A version check is needed.
|
|
||||||
if test -n "$PYTHON"; then
|
|
||||||
# If the user set $PYTHON, use it and don't search something else.
|
|
||||||
AC_MSG_CHECKING([whether $PYTHON version is >= $1])
|
|
||||||
AM_PYTHON_CHECK_VERSION([$PYTHON], [$1],
|
|
||||||
[AC_MSG_RESULT([yes])],
|
|
||||||
[AC_MSG_RESULT([no])
|
|
||||||
AC_MSG_ERROR([Python interpreter is too old])])
|
|
||||||
am_display_PYTHON=$PYTHON
|
|
||||||
else
|
|
||||||
# Otherwise, try each interpreter until we find one that satisfies
|
|
||||||
# VERSION.
|
|
||||||
AC_CACHE_CHECK([for a Python interpreter with version >= $1],
|
|
||||||
[am_cv_pathless_PYTHON],[
|
|
||||||
for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do
|
|
||||||
test "$am_cv_pathless_PYTHON" = none && break
|
|
||||||
AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break])
|
|
||||||
done])
|
|
||||||
# Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
|
|
||||||
if test "$am_cv_pathless_PYTHON" = none; then
|
|
||||||
PYTHON=:
|
|
||||||
else
|
|
||||||
AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON])
|
|
||||||
fi
|
|
||||||
am_display_PYTHON=$am_cv_pathless_PYTHON
|
|
||||||
fi
|
|
||||||
])
|
|
||||||
|
|
||||||
if test "$PYTHON" = :; then
|
|
||||||
dnl Run any user-specified action, or abort.
|
|
||||||
m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])])
|
|
||||||
else
|
|
||||||
|
|
||||||
dnl Query Python for its version number. Getting [:3] seems to be
|
|
||||||
dnl the best way to do this; it's what "site.py" does in the standard
|
|
||||||
dnl library.
|
|
||||||
|
|
||||||
AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version],
|
|
||||||
[am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`])
|
|
||||||
AC_SUBST([PYTHON_VERSION], [$am_cv_python_version])
|
|
||||||
|
|
||||||
dnl Use the values of $prefix and $exec_prefix for the corresponding
|
|
||||||
dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made
|
|
||||||
dnl distinct variables so they can be overridden if need be. However,
|
|
||||||
dnl general consensus is that you shouldn't need this ability.
|
|
||||||
|
|
||||||
AC_SUBST([PYTHON_PREFIX], ['${prefix}'])
|
|
||||||
AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}'])
|
|
||||||
|
|
||||||
dnl At times (like when building shared libraries) you may want
|
|
||||||
dnl to know which OS platform Python thinks this is.
|
|
||||||
|
|
||||||
AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform],
|
|
||||||
[am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`])
|
|
||||||
AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform])
|
|
||||||
|
|
||||||
# Just factor out some code duplication.
|
|
||||||
am_python_setup_sysconfig="\
|
|
||||||
import sys
|
|
||||||
# Prefer sysconfig over distutils.sysconfig, for better compatibility
|
|
||||||
# with python 3.x. See automake bug#10227.
|
|
||||||
try:
|
|
||||||
import sysconfig
|
|
||||||
except ImportError:
|
|
||||||
can_use_sysconfig = 0
|
|
||||||
else:
|
|
||||||
can_use_sysconfig = 1
|
|
||||||
# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs:
|
|
||||||
# <https://github.com/pypa/virtualenv/issues/118>
|
|
||||||
try:
|
|
||||||
from platform import python_implementation
|
|
||||||
if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7':
|
|
||||||
can_use_sysconfig = 0
|
|
||||||
except ImportError:
|
|
||||||
pass"
|
|
||||||
|
|
||||||
dnl Set up 4 directories:
|
|
||||||
|
|
||||||
dnl pythondir -- where to install python scripts. This is the
|
|
||||||
dnl site-packages directory, not the python standard library
|
|
||||||
dnl directory like in previous automake betas. This behavior
|
|
||||||
dnl is more consistent with lispdir.m4 for example.
|
|
||||||
dnl Query distutils for this directory.
|
|
||||||
AC_CACHE_CHECK([for $am_display_PYTHON script directory],
|
|
||||||
[am_cv_python_pythondir],
|
|
||||||
[if test "x$prefix" = xNONE
|
|
||||||
then
|
|
||||||
am_py_prefix=$ac_default_prefix
|
|
||||||
else
|
|
||||||
am_py_prefix=$prefix
|
|
||||||
fi
|
|
||||||
am_cv_python_pythondir=`$PYTHON -c "
|
|
||||||
$am_python_setup_sysconfig
|
|
||||||
if can_use_sysconfig:
|
|
||||||
sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'})
|
|
||||||
else:
|
|
||||||
from distutils import sysconfig
|
|
||||||
sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix')
|
|
||||||
sys.stdout.write(sitedir)"`
|
|
||||||
case $am_cv_python_pythondir in
|
|
||||||
$am_py_prefix*)
|
|
||||||
am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'`
|
|
||||||
am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"`
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
case $am_py_prefix in
|
|
||||||
/usr|/System*) ;;
|
|
||||||
*)
|
|
||||||
am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
])
|
|
||||||
AC_SUBST([pythondir], [$am_cv_python_pythondir])
|
|
||||||
|
|
||||||
dnl pkgpythondir -- $PACKAGE directory under pythondir. Was
|
|
||||||
dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is
|
|
||||||
dnl more consistent with the rest of automake.
|
|
||||||
|
|
||||||
AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE])
|
|
||||||
|
|
||||||
dnl pyexecdir -- directory for installing python extension modules
|
|
||||||
dnl (shared libraries)
|
|
||||||
dnl Query distutils for this directory.
|
|
||||||
AC_CACHE_CHECK([for $am_display_PYTHON extension module directory],
|
|
||||||
[am_cv_python_pyexecdir],
|
|
||||||
[if test "x$exec_prefix" = xNONE
|
|
||||||
then
|
|
||||||
am_py_exec_prefix=$am_py_prefix
|
|
||||||
else
|
|
||||||
am_py_exec_prefix=$exec_prefix
|
|
||||||
fi
|
|
||||||
am_cv_python_pyexecdir=`$PYTHON -c "
|
|
||||||
$am_python_setup_sysconfig
|
|
||||||
if can_use_sysconfig:
|
|
||||||
sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'})
|
|
||||||
else:
|
|
||||||
from distutils import sysconfig
|
|
||||||
sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix')
|
|
||||||
sys.stdout.write(sitedir)"`
|
|
||||||
case $am_cv_python_pyexecdir in
|
|
||||||
$am_py_exec_prefix*)
|
|
||||||
am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'`
|
|
||||||
am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"`
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
case $am_py_exec_prefix in
|
|
||||||
/usr|/System*) ;;
|
|
||||||
*)
|
|
||||||
am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
])
|
|
||||||
AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir])
|
|
||||||
|
|
||||||
dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE)
|
|
||||||
|
|
||||||
AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE])
|
|
||||||
|
|
||||||
dnl Run any user-specified action.
|
|
||||||
$2
|
|
||||||
fi
|
|
||||||
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION.
|
|
||||||
# Run ACTION-IF-FALSE otherwise.
|
|
||||||
# This test uses sys.hexversion instead of the string equivalent (first
|
|
||||||
# word of sys.version), in order to cope with versions such as 2.2c1.
|
|
||||||
# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000).
|
|
||||||
AC_DEFUN([AM_PYTHON_CHECK_VERSION],
|
|
||||||
[prog="import sys
|
|
||||||
# split strings by '.' and convert to numeric. Append some zeros
|
|
||||||
# because we need at least 4 digits for the hex conversion.
|
|
||||||
# map returns an iterator in Python 3.0 and a list in 2.x
|
|
||||||
minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]]
|
|
||||||
minverhex = 0
|
|
||||||
# xrange is not present in Python 3.0 and range returns an iterator
|
|
||||||
for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]]
|
|
||||||
sys.exit(sys.hexversion < minverhex)"
|
|
||||||
AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])])
|
|
||||||
|
|
||||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
|
||||||
#
|
|
||||||
# This file is free software; the Free Software Foundation
|
|
||||||
# gives unlimited permission to copy and/or distribute it,
|
|
||||||
# with or without modifications, as long as this notice is preserved.
|
|
||||||
|
|
||||||
# AM_RUN_LOG(COMMAND)
|
|
||||||
# -------------------
|
|
||||||
# Run COMMAND, save the exit status in ac_status, and log it.
|
|
||||||
# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
|
|
||||||
AC_DEFUN([AM_RUN_LOG],
|
|
||||||
[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
|
|
||||||
($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
|
|
||||||
ac_status=$?
|
|
||||||
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
|
|
||||||
(exit $ac_status); }])
|
|
||||||
|
|
||||||
|
|
||||||
m4_include([acinclude.m4])
|
|
||||||
1325
autoconf/config.guess
vendored
1325
autoconf/config.guess
vendored
File diff suppressed because it is too large
Load Diff
718
autoconf/config.sub
vendored
718
autoconf/config.sub
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,62 +1,36 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
#
|
||||||
# install - install a program, script, or datafile
|
# install - install a program, script, or datafile
|
||||||
|
# This comes from X11R5 (mit/util/scripts/install.sh).
|
||||||
scriptversion=2006-10-14.15
|
|
||||||
|
|
||||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
|
||||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
|
||||||
# following copyright and license.
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 1994 X Consortium
|
# Copyright 1991 by the Massachusetts Institute of Technology
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission to use, copy, modify, distribute, and sell this software and its
|
||||||
# of this software and associated documentation files (the "Software"), to
|
# documentation for any purpose is hereby granted without fee, provided that
|
||||||
# deal in the Software without restriction, including without limitation the
|
# the above copyright notice appear in all copies and that both that
|
||||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
# copyright notice and this permission notice appear in supporting
|
||||||
# sell copies of the Software, and to permit persons to whom the Software is
|
# documentation, and that the name of M.I.T. not be used in advertising or
|
||||||
# furnished to do so, subject to the following conditions:
|
# publicity pertaining to distribution of the software without specific,
|
||||||
#
|
# written prior permission. M.I.T. makes no representations about the
|
||||||
# The above copyright notice and this permission notice shall be included in
|
# suitability of this software for any purpose. It is provided "as is"
|
||||||
# all copies or substantial portions of the Software.
|
# without express or implied warranty.
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
|
||||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
#
|
|
||||||
# Except as contained in this notice, the name of the X Consortium shall not
|
|
||||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
|
||||||
# ings in this Software without prior written authorization from the X Consor-
|
|
||||||
# tium.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# FSF changes to this file are in the public domain.
|
|
||||||
#
|
#
|
||||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||||
# `make' implicit rules from creating a file called install from it
|
# `make' implicit rules from creating a file called install from it
|
||||||
# when there is no Makefile.
|
# when there is no Makefile.
|
||||||
#
|
#
|
||||||
# This script is compatible with the BSD install script, but was written
|
# This script is compatible with the BSD install script, but was written
|
||||||
# from scratch.
|
# from scratch. It can only install one file at a time, a restriction
|
||||||
|
# shared with many OS's install programs.
|
||||||
|
|
||||||
nl='
|
|
||||||
'
|
|
||||||
IFS=" "" $nl"
|
|
||||||
|
|
||||||
# set DOITPROG to echo to test this script
|
# set DOITPROG to echo to test this script
|
||||||
|
|
||||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||||
doit="${DOITPROG-}"
|
doit="${DOITPROG-}"
|
||||||
if test -z "$doit"; then
|
|
||||||
doit_exec=exec
|
|
||||||
else
|
|
||||||
doit_exec=$doit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Put in absolute file names if you don't have them in your path;
|
|
||||||
# or use environment vars.
|
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||||
|
|
||||||
mvprog="${MVPROG-mv}"
|
mvprog="${MVPROG-mv}"
|
||||||
cpprog="${CPPROG-cp}"
|
cpprog="${CPPROG-cp}"
|
||||||
@@ -67,441 +41,211 @@ stripprog="${STRIPPROG-strip}"
|
|||||||
rmprog="${RMPROG-rm}"
|
rmprog="${RMPROG-rm}"
|
||||||
mkdirprog="${MKDIRPROG-mkdir}"
|
mkdirprog="${MKDIRPROG-mkdir}"
|
||||||
|
|
||||||
posix_glob=
|
transformbasename=""
|
||||||
posix_mkdir=
|
transform_arg=""
|
||||||
|
instcmd="$mvprog"
|
||||||
# Desired mode of installed file.
|
chmodcmd="$chmodprog 0755"
|
||||||
mode=0755
|
chowncmd=""
|
||||||
|
chgrpcmd=""
|
||||||
chmodcmd=$chmodprog
|
stripcmd=""
|
||||||
chowncmd=
|
|
||||||
chgrpcmd=
|
|
||||||
stripcmd=
|
|
||||||
rmcmd="$rmprog -f"
|
rmcmd="$rmprog -f"
|
||||||
mvcmd="$mvprog"
|
mvcmd="$mvprog"
|
||||||
src=
|
src=""
|
||||||
dst=
|
dst=""
|
||||||
dir_arg=
|
dir_arg=""
|
||||||
dstarg=
|
|
||||||
no_target_directory=
|
|
||||||
|
|
||||||
usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
while [ x"$1" != x ]; do
|
||||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
case $1 in
|
||||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
-c) instcmd="$cpprog"
|
||||||
or: $0 [OPTION]... -d DIRECTORIES...
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
In the 1st form, copy SRCFILE to DSTFILE.
|
-d) dir_arg=true
|
||||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
shift
|
||||||
In the 4th, create DIRECTORIES.
|
continue;;
|
||||||
|
|
||||||
Options:
|
-m) chmodcmd="$chmodprog $2"
|
||||||
-c (ignored)
|
shift
|
||||||
-d create directories instead of installing files.
|
shift
|
||||||
-g GROUP $chgrpprog installed files to GROUP.
|
continue;;
|
||||||
-m MODE $chmodprog installed files to MODE.
|
|
||||||
-o USER $chownprog installed files to USER.
|
|
||||||
-s $stripprog installed files.
|
|
||||||
-t DIRECTORY install into DIRECTORY.
|
|
||||||
-T report an error if DSTFILE is a directory.
|
|
||||||
--help display this help and exit.
|
|
||||||
--version display version info and exit.
|
|
||||||
|
|
||||||
Environment variables override the default commands:
|
-o) chowncmd="$chownprog $2"
|
||||||
CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
|
shift
|
||||||
"
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
while test $# -ne 0; do
|
-g) chgrpcmd="$chgrpprog $2"
|
||||||
case $1 in
|
shift
|
||||||
-c) shift
|
shift
|
||||||
continue;;
|
continue;;
|
||||||
|
|
||||||
-d) dir_arg=true
|
-s) stripcmd="$stripprog"
|
||||||
shift
|
shift
|
||||||
continue;;
|
continue;;
|
||||||
|
|
||||||
-g) chgrpcmd="$chgrpprog $2"
|
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
|
||||||
shift
|
shift
|
||||||
shift
|
continue;;
|
||||||
continue;;
|
|
||||||
|
|
||||||
--help) echo "$usage"; exit $?;;
|
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
-m) mode=$2
|
*) if [ x"$src" = x ]
|
||||||
shift
|
|
||||||
shift
|
|
||||||
case $mode in
|
|
||||||
*' '* | *' '* | *'
|
|
||||||
'* | *'*'* | *'?'* | *'['*)
|
|
||||||
echo "$0: invalid mode: $mode" >&2
|
|
||||||
exit 1;;
|
|
||||||
esac
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-o) chowncmd="$chownprog $2"
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-s) stripcmd=$stripprog
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-t) dstarg=$2
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-T) no_target_directory=true
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
--version) echo "$0 $scriptversion"; exit $?;;
|
|
||||||
|
|
||||||
--) shift
|
|
||||||
break;;
|
|
||||||
|
|
||||||
-*) echo "$0: invalid option: $1" >&2
|
|
||||||
exit 1;;
|
|
||||||
|
|
||||||
*) break;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if test $# -ne 0 && test -z "$dir_arg$dstarg"; then
|
|
||||||
# When -d is used, all remaining arguments are directories to create.
|
|
||||||
# When -t is used, the destination is already specified.
|
|
||||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
|
||||||
for arg
|
|
||||||
do
|
|
||||||
if test -n "$dstarg"; then
|
|
||||||
# $@ is not empty: it contains at least $arg.
|
|
||||||
set fnord "$@" "$dstarg"
|
|
||||||
shift # fnord
|
|
||||||
fi
|
|
||||||
shift # arg
|
|
||||||
dstarg=$arg
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test $# -eq 0; then
|
|
||||||
if test -z "$dir_arg"; then
|
|
||||||
echo "$0: no input file specified." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# It's OK to call `install-sh -d' without argument.
|
|
||||||
# This can happen when creating conditional directories.
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -z "$dir_arg"; then
|
|
||||||
trap '(exit $?); exit' 1 2 13 15
|
|
||||||
|
|
||||||
# Set umask so as not to create temps with too-generous modes.
|
|
||||||
# However, 'strip' requires both read and write access to temps.
|
|
||||||
case $mode in
|
|
||||||
# Optimize common cases.
|
|
||||||
*644) cp_umask=133;;
|
|
||||||
*755) cp_umask=22;;
|
|
||||||
|
|
||||||
*[0-7])
|
|
||||||
if test -z "$stripcmd"; then
|
|
||||||
u_plus_rw=
|
|
||||||
else
|
|
||||||
u_plus_rw='% 200'
|
|
||||||
fi
|
|
||||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
|
||||||
*)
|
|
||||||
if test -z "$stripcmd"; then
|
|
||||||
u_plus_rw=
|
|
||||||
else
|
|
||||||
u_plus_rw=,u+rw
|
|
||||||
fi
|
|
||||||
cp_umask=$mode$u_plus_rw;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
for src
|
|
||||||
do
|
|
||||||
# Protect names starting with `-'.
|
|
||||||
case $src in
|
|
||||||
-*) src=./$src ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if test -n "$dir_arg"; then
|
|
||||||
dst=$src
|
|
||||||
dstdir=$dst
|
|
||||||
test -d "$dstdir"
|
|
||||||
dstdir_status=$?
|
|
||||||
else
|
|
||||||
|
|
||||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
|
||||||
# might cause directories to be created, which would be especially bad
|
|
||||||
# if $src (and thus $dsttmp) contains '*'.
|
|
||||||
if test ! -f "$src" && test ! -d "$src"; then
|
|
||||||
echo "$0: $src does not exist." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -z "$dstarg"; then
|
|
||||||
echo "$0: no destination specified." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
dst=$dstarg
|
|
||||||
# Protect names starting with `-'.
|
|
||||||
case $dst in
|
|
||||||
-*) dst=./$dst ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# If destination is a directory, append the input filename; won't work
|
|
||||||
# if double slashes aren't ignored.
|
|
||||||
if test -d "$dst"; then
|
|
||||||
if test -n "$no_target_directory"; then
|
|
||||||
echo "$0: $dstarg: Is a directory" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
dstdir=$dst
|
|
||||||
dst=$dstdir/`basename "$src"`
|
|
||||||
dstdir_status=0
|
|
||||||
else
|
|
||||||
# Prefer dirname, but fall back on a substitute if dirname fails.
|
|
||||||
dstdir=`
|
|
||||||
(dirname "$dst") 2>/dev/null ||
|
|
||||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
|
||||||
X"$dst" : 'X\(//\)[^/]' \| \
|
|
||||||
X"$dst" : 'X\(//\)$' \| \
|
|
||||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
|
||||||
echo X"$dst" |
|
|
||||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
|
||||||
s//\1/
|
|
||||||
q
|
|
||||||
}
|
|
||||||
/^X\(\/\/\)[^/].*/{
|
|
||||||
s//\1/
|
|
||||||
q
|
|
||||||
}
|
|
||||||
/^X\(\/\/\)$/{
|
|
||||||
s//\1/
|
|
||||||
q
|
|
||||||
}
|
|
||||||
/^X\(\/\).*/{
|
|
||||||
s//\1/
|
|
||||||
q
|
|
||||||
}
|
|
||||||
s/.*/./; q'
|
|
||||||
`
|
|
||||||
|
|
||||||
test -d "$dstdir"
|
|
||||||
dstdir_status=$?
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
obsolete_mkdir_used=false
|
|
||||||
|
|
||||||
if test $dstdir_status != 0; then
|
|
||||||
case $posix_mkdir in
|
|
||||||
'')
|
|
||||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
|
||||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
|
||||||
umask=`umask`
|
|
||||||
case $stripcmd.$umask in
|
|
||||||
# Optimize common cases.
|
|
||||||
*[2367][2367]) mkdir_umask=$umask;;
|
|
||||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
|
||||||
|
|
||||||
*[0-7])
|
|
||||||
mkdir_umask=`expr $umask + 22 \
|
|
||||||
- $umask % 100 % 40 + $umask % 20 \
|
|
||||||
- $umask % 10 % 4 + $umask % 2
|
|
||||||
`;;
|
|
||||||
*) mkdir_umask=$umask,go-w;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# With -d, create the new directory with the user-specified mode.
|
|
||||||
# Otherwise, rely on $mkdir_umask.
|
|
||||||
if test -n "$dir_arg"; then
|
|
||||||
mkdir_mode=-m$mode
|
|
||||||
else
|
|
||||||
mkdir_mode=
|
|
||||||
fi
|
|
||||||
|
|
||||||
posix_mkdir=false
|
|
||||||
case $umask in
|
|
||||||
*[123567][0-7][0-7])
|
|
||||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
|
||||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
|
||||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
|
||||||
|
|
||||||
if (umask $mkdir_umask &&
|
|
||||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
|
||||||
then
|
then
|
||||||
if test -z "$dir_arg" || {
|
src=$1
|
||||||
# Check for POSIX incompatibilities with -m.
|
|
||||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
|
||||||
# other-writeable bit of parent directory when it shouldn't.
|
|
||||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
|
||||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
|
||||||
case $ls_ld_tmpdir in
|
|
||||||
d????-?r-*) different_mode=700;;
|
|
||||||
d????-?--*) different_mode=755;;
|
|
||||||
*) false;;
|
|
||||||
esac &&
|
|
||||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
|
||||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
|
||||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
then posix_mkdir=:
|
|
||||||
fi
|
|
||||||
rmdir "$tmpdir/d" "$tmpdir"
|
|
||||||
else
|
else
|
||||||
# Remove any dirs left behind by ancient mkdir implementations.
|
# this colon is to work around a 386BSD /bin/sh bug
|
||||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
:
|
||||||
|
dst=$1
|
||||||
fi
|
fi
|
||||||
trap '' 0;;
|
shift
|
||||||
esac;;
|
continue;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if
|
|
||||||
$posix_mkdir && (
|
|
||||||
umask $mkdir_umask &&
|
|
||||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
|
||||||
)
|
|
||||||
then :
|
|
||||||
else
|
|
||||||
|
|
||||||
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
|
||||||
# or it failed possibly due to a race condition. Create the
|
|
||||||
# directory the slow way, step by step, checking for races as we go.
|
|
||||||
|
|
||||||
case $dstdir in
|
|
||||||
/*) prefix=/ ;;
|
|
||||||
-*) prefix=./ ;;
|
|
||||||
*) prefix= ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case $posix_glob in
|
|
||||||
'')
|
|
||||||
if (set -f) 2>/dev/null; then
|
|
||||||
posix_glob=true
|
|
||||||
else
|
|
||||||
posix_glob=false
|
|
||||||
fi ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
oIFS=$IFS
|
|
||||||
IFS=/
|
|
||||||
$posix_glob && set -f
|
|
||||||
set fnord $dstdir
|
|
||||||
shift
|
|
||||||
$posix_glob && set +f
|
|
||||||
IFS=$oIFS
|
|
||||||
|
|
||||||
prefixes=
|
|
||||||
|
|
||||||
for d
|
|
||||||
do
|
|
||||||
test -z "$d" && continue
|
|
||||||
|
|
||||||
prefix=$prefix$d
|
|
||||||
if test -d "$prefix"; then
|
|
||||||
prefixes=
|
|
||||||
else
|
|
||||||
if $posix_mkdir; then
|
|
||||||
(umask=$mkdir_umask &&
|
|
||||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
|
||||||
# Don't fail if two instances are running concurrently.
|
|
||||||
test -d "$prefix" || exit 1
|
|
||||||
else
|
|
||||||
case $prefix in
|
|
||||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
|
||||||
*) qprefix=$prefix;;
|
|
||||||
esac
|
|
||||||
prefixes="$prefixes '$qprefix'"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
prefix=$prefix/
|
|
||||||
done
|
|
||||||
|
|
||||||
if test -n "$prefixes"; then
|
|
||||||
# Don't fail if two instances are running concurrently.
|
|
||||||
(umask $mkdir_umask &&
|
|
||||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
|
||||||
test -d "$dstdir" || exit 1
|
|
||||||
obsolete_mkdir_used=true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -n "$dir_arg"; then
|
|
||||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
|
||||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
|
||||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
|
||||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
|
||||||
else
|
|
||||||
|
|
||||||
# Make a couple of temp file names in the proper directory.
|
|
||||||
dsttmp=$dstdir/_inst.$$_
|
|
||||||
rmtmp=$dstdir/_rm.$$_
|
|
||||||
|
|
||||||
# Trap to clean up those temp files at exit.
|
|
||||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
|
||||||
|
|
||||||
# Copy the file name to the temp name.
|
|
||||||
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
|
||||||
|
|
||||||
# and set any options; do chmod last to preserve setuid bits.
|
|
||||||
#
|
|
||||||
# If any of these fail, we abort the whole thing. If we want to
|
|
||||||
# ignore errors from any of these, just make sure not to ignore
|
|
||||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
|
||||||
#
|
|
||||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
|
|
||||||
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
|
|
||||||
&& { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
|
|
||||||
&& { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
|
||||||
|
|
||||||
# Now rename the file to the real destination.
|
|
||||||
{ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \
|
|
||||||
|| {
|
|
||||||
# The rename failed, perhaps because mv can't rename something else
|
|
||||||
# to itself, or perhaps because mv is so ancient that it does not
|
|
||||||
# support -f.
|
|
||||||
|
|
||||||
# Now remove or move aside any old file at destination location.
|
|
||||||
# We try this two ways since rm can't unlink itself on some
|
|
||||||
# systems and the destination file might be busy for other
|
|
||||||
# reasons. In this case, the final cleanup might fail but the new
|
|
||||||
# file should still install successfully.
|
|
||||||
{
|
|
||||||
if test -f "$dst"; then
|
|
||||||
$doit $rmcmd -f "$dst" 2>/dev/null \
|
|
||||||
|| { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \
|
|
||||||
&& { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\
|
|
||||||
|| {
|
|
||||||
echo "$0: cannot unlink or rename $dst" >&2
|
|
||||||
(exit 1); exit 1
|
|
||||||
}
|
|
||||||
else
|
|
||||||
:
|
|
||||||
fi
|
|
||||||
} &&
|
|
||||||
|
|
||||||
# Now rename the file to the real destination.
|
|
||||||
$doit $mvcmd "$dsttmp" "$dst"
|
|
||||||
}
|
|
||||||
} || exit 1
|
|
||||||
|
|
||||||
trap '' 0
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# Local variables:
|
if [ x"$src" = x ]
|
||||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
then
|
||||||
# time-stamp-start: "scriptversion="
|
echo "install: no input file specified"
|
||||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
exit 1
|
||||||
# time-stamp-end: "$"
|
else
|
||||||
# End:
|
true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ x"$dir_arg" != x ]; then
|
||||||
|
dst=$src
|
||||||
|
src=""
|
||||||
|
|
||||||
|
if [ -d $dst ]; then
|
||||||
|
instcmd=:
|
||||||
|
chmodcmd=""
|
||||||
|
else
|
||||||
|
instcmd=mkdir
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
|
||||||
|
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
|
||||||
|
# might cause directories to be created, which would be especially bad
|
||||||
|
# if $src (and thus $dsttmp) contains '*'.
|
||||||
|
|
||||||
|
if [ -f $src -o -d $src ]
|
||||||
|
then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
echo "install: $src does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ x"$dst" = x ]
|
||||||
|
then
|
||||||
|
echo "install: no destination specified"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If destination is a directory, append the input filename; if your system
|
||||||
|
# does not like double slashes in filenames, you may need to add some logic
|
||||||
|
|
||||||
|
if [ -d $dst ]
|
||||||
|
then
|
||||||
|
dst="$dst"/`basename $src`
|
||||||
|
else
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
## this sed command emulates the dirname command
|
||||||
|
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
|
||||||
|
|
||||||
|
# Make sure that the destination directory exists.
|
||||||
|
# this part is taken from Noah Friedman's mkinstalldirs script
|
||||||
|
|
||||||
|
# Skip lots of stat calls in the usual case.
|
||||||
|
if [ ! -d "$dstdir" ]; then
|
||||||
|
defaultIFS='
|
||||||
|
'
|
||||||
|
IFS="${IFS-${defaultIFS}}"
|
||||||
|
|
||||||
|
oIFS="${IFS}"
|
||||||
|
# Some sh's can't handle IFS=/ for some reason.
|
||||||
|
IFS='%'
|
||||||
|
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
|
||||||
|
IFS="${oIFS}"
|
||||||
|
|
||||||
|
pathcomp=''
|
||||||
|
|
||||||
|
while [ $# -ne 0 ] ; do
|
||||||
|
pathcomp="${pathcomp}${1}"
|
||||||
|
shift
|
||||||
|
|
||||||
|
if [ ! -d "${pathcomp}" ] ;
|
||||||
|
then
|
||||||
|
$mkdirprog "${pathcomp}"
|
||||||
|
else
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
|
||||||
|
pathcomp="${pathcomp}/"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ x"$dir_arg" != x ]
|
||||||
|
then
|
||||||
|
$doit $instcmd $dst &&
|
||||||
|
|
||||||
|
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
|
||||||
|
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
|
||||||
|
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
|
||||||
|
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
|
||||||
|
else
|
||||||
|
|
||||||
|
# If we're going to rename the final executable, determine the name now.
|
||||||
|
|
||||||
|
if [ x"$transformarg" = x ]
|
||||||
|
then
|
||||||
|
dstfile=`basename $dst`
|
||||||
|
else
|
||||||
|
dstfile=`basename $dst $transformbasename |
|
||||||
|
sed $transformarg`$transformbasename
|
||||||
|
fi
|
||||||
|
|
||||||
|
# don't allow the sed command to completely eliminate the filename
|
||||||
|
|
||||||
|
if [ x"$dstfile" = x ]
|
||||||
|
then
|
||||||
|
dstfile=`basename $dst`
|
||||||
|
else
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make a temp file name in the proper directory.
|
||||||
|
|
||||||
|
dsttmp=$dstdir/#inst.$$#
|
||||||
|
|
||||||
|
# Move or copy the file name to the temp name
|
||||||
|
|
||||||
|
$doit $instcmd $src $dsttmp &&
|
||||||
|
|
||||||
|
trap "rm -f ${dsttmp}" 0 &&
|
||||||
|
|
||||||
|
# and set any options; do chmod last to preserve setuid bits
|
||||||
|
|
||||||
|
# If any of these fail, we abort the whole thing. If we want to
|
||||||
|
# ignore errors from any of these, just make sure not to ignore
|
||||||
|
# errors from the above "$doit $instcmd $src $dsttmp" command.
|
||||||
|
|
||||||
|
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
|
||||||
|
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
|
||||||
|
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
|
||||||
|
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
|
||||||
|
|
||||||
|
# Now rename the file to the real destination.
|
||||||
|
|
||||||
|
$doit $rmcmd -f $dstdir/$dstfile &&
|
||||||
|
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
||||||
|
|
||||||
|
fi &&
|
||||||
|
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# py-compile - Compile a Python program
|
|
||||||
|
|
||||||
scriptversion=2011-06-08.12; # UTC
|
|
||||||
|
|
||||||
# Copyright (C) 2000-2014 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# As a special exception to the GNU General Public License, if you
|
|
||||||
# distribute this file as part of a program that contains a
|
|
||||||
# configuration script generated by Autoconf, you may include it under
|
|
||||||
# the same distribution terms that you use for the rest of that program.
|
|
||||||
|
|
||||||
# This file is maintained in Automake, please report
|
|
||||||
# bugs to <bug-automake@gnu.org> or send patches to
|
|
||||||
# <automake-patches@gnu.org>.
|
|
||||||
|
|
||||||
if [ -z "$PYTHON" ]; then
|
|
||||||
PYTHON=python
|
|
||||||
fi
|
|
||||||
|
|
||||||
me=py-compile
|
|
||||||
|
|
||||||
usage_error ()
|
|
||||||
{
|
|
||||||
echo "$me: $*" >&2
|
|
||||||
echo "Try '$me --help' for more information." >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
basedir=
|
|
||||||
destdir=
|
|
||||||
while test $# -ne 0; do
|
|
||||||
case "$1" in
|
|
||||||
--basedir)
|
|
||||||
if test $# -lt 2; then
|
|
||||||
usage_error "option '--basedir' requires an argument"
|
|
||||||
else
|
|
||||||
basedir=$2
|
|
||||||
fi
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--destdir)
|
|
||||||
if test $# -lt 2; then
|
|
||||||
usage_error "option '--destdir' requires an argument"
|
|
||||||
else
|
|
||||||
destdir=$2
|
|
||||||
fi
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-h|--help)
|
|
||||||
cat <<\EOF
|
|
||||||
Usage: py-compile [--help] [--version] [--basedir DIR] [--destdir DIR] FILES..."
|
|
||||||
|
|
||||||
Byte compile some python scripts FILES. Use --destdir to specify any
|
|
||||||
leading directory path to the FILES that you don't want to include in the
|
|
||||||
byte compiled file. Specify --basedir for any additional path information you
|
|
||||||
do want to be shown in the byte compiled file.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
py-compile --destdir /tmp/pkg-root --basedir /usr/share/test test.py test2.py
|
|
||||||
|
|
||||||
Report bugs to <bug-automake@gnu.org>.
|
|
||||||
EOF
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
-v|--version)
|
|
||||||
echo "$me $scriptversion"
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
shift
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-*)
|
|
||||||
usage_error "unrecognized option '$1'"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
files=$*
|
|
||||||
if test -z "$files"; then
|
|
||||||
usage_error "no files given"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if basedir was given, then it should be prepended to filenames before
|
|
||||||
# byte compilation.
|
|
||||||
if [ -z "$basedir" ]; then
|
|
||||||
pathtrans="path = file"
|
|
||||||
else
|
|
||||||
pathtrans="path = os.path.join('$basedir', file)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if destdir was given, then it needs to be prepended to the filename to
|
|
||||||
# byte compile but not go into the compiled file.
|
|
||||||
if [ -z "$destdir" ]; then
|
|
||||||
filetrans="filepath = path"
|
|
||||||
else
|
|
||||||
filetrans="filepath = os.path.normpath('$destdir' + os.sep + path)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
$PYTHON -c "
|
|
||||||
import sys, os, py_compile, imp
|
|
||||||
|
|
||||||
files = '''$files'''
|
|
||||||
|
|
||||||
sys.stdout.write('Byte-compiling python modules...\n')
|
|
||||||
for file in files.split():
|
|
||||||
$pathtrans
|
|
||||||
$filetrans
|
|
||||||
if not os.path.exists(filepath) or not (len(filepath) >= 3
|
|
||||||
and filepath[-3:] == '.py'):
|
|
||||||
continue
|
|
||||||
sys.stdout.write(file)
|
|
||||||
sys.stdout.flush()
|
|
||||||
if hasattr(imp, 'get_tag'):
|
|
||||||
py_compile.compile(filepath, imp.cache_from_source(filepath), path)
|
|
||||||
else:
|
|
||||||
py_compile.compile(filepath, filepath + 'c', path)
|
|
||||||
sys.stdout.write('\n')" || exit $?
|
|
||||||
|
|
||||||
# this will fail for python < 1.5, but that doesn't matter ...
|
|
||||||
$PYTHON -O -c "
|
|
||||||
import sys, os, py_compile, imp
|
|
||||||
|
|
||||||
# pypy does not use .pyo optimization
|
|
||||||
if hasattr(sys, 'pypy_translation_info'):
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
files = '''$files'''
|
|
||||||
sys.stdout.write('Byte-compiling python modules (optimized versions) ...\n')
|
|
||||||
for file in files.split():
|
|
||||||
$pathtrans
|
|
||||||
$filetrans
|
|
||||||
if not os.path.exists(filepath) or not (len(filepath) >= 3
|
|
||||||
and filepath[-3:] == '.py'):
|
|
||||||
continue
|
|
||||||
sys.stdout.write(file)
|
|
||||||
sys.stdout.flush()
|
|
||||||
if hasattr(imp, 'get_tag'):
|
|
||||||
py_compile.compile(filepath, imp.cache_from_source(filepath, False), path)
|
|
||||||
else:
|
|
||||||
py_compile.compile(filepath, filepath + 'o', path)
|
|
||||||
sys.stdout.write('\n')" 2>/dev/null || :
|
|
||||||
|
|
||||||
# Local Variables:
|
|
||||||
# mode: shell-script
|
|
||||||
# sh-indentation: 2
|
|
||||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
|
||||||
# time-stamp-start: "scriptversion="
|
|
||||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
|
||||||
# time-stamp-time-zone: "UTC"
|
|
||||||
# time-stamp-end: "; # UTC"
|
|
||||||
# End:
|
|
||||||
6
conf/.gitignore
vendored
6
conf/.gitignore
vendored
@@ -1,6 +0,0 @@
|
|||||||
command_profile_template.profile
|
|
||||||
example.conf
|
|
||||||
lvmlocal.conf
|
|
||||||
metadata_profile_template.profile
|
|
||||||
configure.h
|
|
||||||
lvm-version.h
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
CONFSRC=example.conf
|
|
||||||
CONFDEST=lvm.conf
|
|
||||||
CONFLOCAL=lvmlocal.conf
|
|
||||||
|
|
||||||
PROFILE_TEMPLATES=command_profile_template.profile metadata_profile_template.profile
|
|
||||||
PROFILES=$(PROFILE_TEMPLATES) \
|
|
||||||
$(srcdir)/cache-mq.profile \
|
|
||||||
$(srcdir)/cache-smq.profile \
|
|
||||||
$(srcdir)/thin-generic.profile \
|
|
||||||
$(srcdir)/thin-performance.profile \
|
|
||||||
$(srcdir)/lvmdbusd.profile
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
.PHONY: install_conf install_localconf install_profiles
|
|
||||||
|
|
||||||
generate:
|
|
||||||
LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withgeneralpreamble --withcomments --ignorelocal --withspaces > example.conf.in
|
|
||||||
LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withlocalpreamble --withcomments --withspaces local > lvmlocal.conf.in
|
|
||||||
|
|
||||||
install_conf: $(CONFSRC)
|
|
||||||
@if [ ! -e $(confdir)/$(CONFDEST) ]; then \
|
|
||||||
echo "$(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST)"; \
|
|
||||||
$(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST); \
|
|
||||||
fi
|
|
||||||
|
|
||||||
install_localconf: $(CONFLOCAL)
|
|
||||||
@if [ ! -e $(confdir)/$(CONFLOCAL) ]; then \
|
|
||||||
echo "$(INSTALL_WDATA) -D $< $(confdir)/$(CONFLOCAL)"; \
|
|
||||||
$(INSTALL_WDATA) -D $< $(confdir)/$(CONFLOCAL); \
|
|
||||||
fi
|
|
||||||
|
|
||||||
install_profiles: $(PROFILES)
|
|
||||||
$(INSTALL_DIR) $(profiledir)
|
|
||||||
$(INSTALL_DATA) $(PROFILES) $(profiledir)/
|
|
||||||
|
|
||||||
install_lvm2: install_conf install_localconf install_profiles
|
|
||||||
|
|
||||||
install: install_lvm2
|
|
||||||
|
|
||||||
DISTCLEAN_TARGETS += $(CONFSRC) $(CONFLOCAL) $(PROFILE_TEMPLATES)
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# 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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# Demo configuration 'smq' cache policy
|
|
||||||
#
|
|
||||||
# The stochastic multi-queue (smq) policy addresses some of the problems
|
|
||||||
# with the multiqueue (mq) policy and uses less memory.
|
|
||||||
#
|
|
||||||
|
|
||||||
allocation {
|
|
||||||
cache_pool_chunk_size = 64
|
|
||||||
cache_mode = "writethrough"
|
|
||||||
cache_policy = "smq"
|
|
||||||
cache_settings {
|
|
||||||
# currently no settings for "smq" policy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
# This is a command profile template for the LVM2 system.
|
|
||||||
#
|
|
||||||
# It contains all configuration settings that are customizable by command
|
|
||||||
# profiles. To create a new command profile, select the settings you want
|
|
||||||
# to customize and add them in a new file named <profile_name>.profile.
|
|
||||||
# Then install the new profile in a directory as defined by config/profile_dir
|
|
||||||
# setting found in @DEFAULT_SYS_DIR@/lvm.conf file.
|
|
||||||
#
|
|
||||||
# Command profiles can be referenced by using the --commandprofile option then.
|
|
||||||
#
|
|
||||||
# Refer to 'man lvm.conf' for further information about profiles and
|
|
||||||
# general configuration file layout.
|
|
||||||
#
|
|
||||||
allocation {
|
|
||||||
cache_mode="writethrough"
|
|
||||||
cache_settings {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log {
|
|
||||||
report_command_log=0
|
|
||||||
command_log_sort="log_seq_num"
|
|
||||||
command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
|
||||||
command_log_selection="!(log_type=status && message=success)"
|
|
||||||
}
|
|
||||||
global {
|
|
||||||
units="h"
|
|
||||||
si_unit_consistency=1
|
|
||||||
suffix=1
|
|
||||||
lvdisplay_shows_full_device_path=0
|
|
||||||
}
|
|
||||||
report {
|
|
||||||
output_format="basic"
|
|
||||||
compact_output=0
|
|
||||||
compact_output_cols=""
|
|
||||||
aligned=1
|
|
||||||
buffered=1
|
|
||||||
headings=1
|
|
||||||
separator=" "
|
|
||||||
list_item_separator=","
|
|
||||||
prefixes=0
|
|
||||||
quoted=1
|
|
||||||
columns_as_rows=0
|
|
||||||
binary_values_as_numeric=0
|
|
||||||
time_format="%Y-%m-%d %T %z"
|
|
||||||
devtypes_sort="devtype_name"
|
|
||||||
devtypes_cols="devtype_name,devtype_max_partitions,devtype_description"
|
|
||||||
devtypes_cols_verbose="devtype_name,devtype_max_partitions,devtype_description"
|
|
||||||
lvs_sort="vg_name,lv_name"
|
|
||||||
lvs_cols="lv_name,vg_name,lv_attr,lv_size,pool_lv,origin,data_percent,metadata_percent,move_pv,mirror_log,copy_percent,convert_lv"
|
|
||||||
lvs_cols_verbose="lv_name,vg_name,seg_count,lv_attr,lv_size,lv_major,lv_minor,lv_kernel_major,lv_kernel_minor,pool_lv,origin,data_percent,metadata_percent,move_pv,copy_percent,mirror_log,convert_lv,lv_uuid,lv_profile"
|
|
||||||
vgs_sort="vg_name"
|
|
||||||
vgs_cols="vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free"
|
|
||||||
vgs_cols_verbose="vg_name,vg_attr,vg_extent_size,pv_count,lv_count,snap_count,vg_size,vg_free,vg_uuid,vg_profile"
|
|
||||||
pvs_sort="pv_name"
|
|
||||||
pvs_cols="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free"
|
|
||||||
pvs_cols_verbose="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,dev_size,pv_uuid"
|
|
||||||
segs_sort="vg_name,lv_name,seg_start"
|
|
||||||
segs_cols="lv_name,vg_name,lv_attr,stripes,segtype,seg_size"
|
|
||||||
segs_cols_verbose="lv_name,vg_name,lv_attr,seg_start,seg_size,stripes,segtype,stripesize,chunksize"
|
|
||||||
pvsegs_sort="pv_name,pvseg_start"
|
|
||||||
pvsegs_cols="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
|
|
||||||
pvsegs_cols_verbose="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
|
||||||
vgs_cols_full="vg_all"
|
|
||||||
pvs_cols_full="pv_all"
|
|
||||||
lvs_cols_full="lv_all"
|
|
||||||
pvsegs_cols_full="pvseg_all,pv_uuid,lv_uuid"
|
|
||||||
segs_cols_full="seg_all,lv_uuid"
|
|
||||||
vgs_sort_full="vg_name"
|
|
||||||
pvs_sort_full="pv_name"
|
|
||||||
lvs_sort_full="vg_name,lv_name"
|
|
||||||
pvsegs_sort_full="pv_uuid,pvseg_start"
|
|
||||||
segs_sort_full="lv_uuid,seg_start"
|
|
||||||
mark_hidden_devices=1
|
|
||||||
}
|
|
||||||
2124
conf/example.conf.in
2124
conf/example.conf.in
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
#
|
|
||||||
# DO NOT EDIT THIS FILE!
|
|
||||||
#
|
|
||||||
# LVM configuration profile used by lvmdbusd daemon.
|
|
||||||
#
|
|
||||||
# This sets up LVM to produce output in the most suitable format for processing
|
|
||||||
# by lvmdbusd daemon which utilizes LVM shell to execute LVM commands.
|
|
||||||
#
|
|
||||||
# Do not edit this file in any way. This profile is distributed together with
|
|
||||||
# lvmdbusd and it contains configuration that is important for lvmdbusd to
|
|
||||||
# cooperate and interface with LVM correctly.
|
|
||||||
#
|
|
||||||
|
|
||||||
global {
|
|
||||||
# use bytes for expected and deterministic output
|
|
||||||
units=b
|
|
||||||
# no need for suffix if we have units set
|
|
||||||
suffix=0
|
|
||||||
}
|
|
||||||
|
|
||||||
report {
|
|
||||||
compact_output=0
|
|
||||||
compact_output_cols=""
|
|
||||||
binary_values_as_numeric=0
|
|
||||||
# time in number of seconds since the Epoch
|
|
||||||
time_format="%s"
|
|
||||||
mark_hidden_devices=1
|
|
||||||
# lvmdbusd expects JSON output
|
|
||||||
output_format=json
|
|
||||||
# *_cols_full for lvm fullreport's fields which lvmdbusd relies on to update its state
|
|
||||||
vgs_cols_full="vg_name,vg_uuid,vg_fmt,vg_size,vg_free,vg_sysid,vg_extent_size,vg_extent_count,vg_free_count,vg_profile,max_lv,max_pv,pv_count,lv_count,snap_count,vg_seqno,vg_mda_count,vg_mda_free,vg_mda_size,vg_mda_used_count,vg_attr,vg_tags"
|
|
||||||
pvs_cols_full="pv_name,pv_uuid,pv_fmt,pv_size,pv_free,pv_used,dev_size,pv_mda_size,pv_mda_free,pv_ba_start,pv_ba_size,pe_start,pv_pe_count,pv_pe_alloc_count,pv_attr,pv_tags,vg_name,vg_uuid"
|
|
||||||
lvs_cols_full="lv_uuid,lv_name,lv_path,lv_size,vg_name,pool_lv_uuid,pool_lv,origin_uuid,origin,data_percent,lv_attr,lv_tags,vg_uuid,lv_active,data_lv,metadata_lv,lv_parent,lv_role,lv_layout"
|
|
||||||
pvsegs_cols_full="pvseg_start,pvseg_size,segtype,pv_uuid,lv_uuid,pv_name"
|
|
||||||
segs_cols_full="seg_pe_ranges,segtype,lv_uuid"
|
|
||||||
vgs_sort_full="vg_name"
|
|
||||||
pvs_sort_full="pv_name"
|
|
||||||
lvs_sort_full="vg_name,lv_name"
|
|
||||||
pvsegs_sort_full="pv_uuid,pvseg_start"
|
|
||||||
segs_sort_full="lv_uuid,seg_start"
|
|
||||||
}
|
|
||||||
|
|
||||||
log {
|
|
||||||
# lvmdbusd relies on command log report to inspect LVM command's execution status
|
|
||||||
report_command_log=1
|
|
||||||
# display only outermost LVM shell-related log that lvmdbusd inspects first after LVM command execution (it calls 'lastlog' for more detailed log afterwards if needed)
|
|
||||||
command_log_selection="log_context=shell"
|
|
||||||
command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
|
||||||
command_log_sort="log_seq_num"
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
# This is a local configuration file template for the LVM2 system
|
|
||||||
# which should be installed as @DEFAULT_SYS_DIR@/lvmlocal.conf .
|
|
||||||
#
|
|
||||||
# Refer to 'man lvm.conf' for information about the file layout.
|
|
||||||
#
|
|
||||||
# To put this file in a different directory and override
|
|
||||||
# @DEFAULT_SYS_DIR@ set the environment variable LVM_SYSTEM_DIR before
|
|
||||||
# running the tools.
|
|
||||||
#
|
|
||||||
# The lvmlocal.conf file is normally expected to contain only the
|
|
||||||
# "local" section which contains settings that should not be shared or
|
|
||||||
# repeated among different hosts. (But if other sections are present,
|
|
||||||
# they *will* get processed. Settings in this file override equivalent
|
|
||||||
# ones in lvm.conf and are in turn overridden by ones in any enabled
|
|
||||||
# lvm_<tag>.conf files.)
|
|
||||||
#
|
|
||||||
# Please take care that each setting only appears once if uncommenting
|
|
||||||
# example settings in this file and never copy this file between hosts.
|
|
||||||
|
|
||||||
|
|
||||||
# Configuration section local.
|
|
||||||
# LVM settings that are specific to the local host.
|
|
||||||
local {
|
|
||||||
|
|
||||||
# Configuration option local/system_id.
|
|
||||||
# Defines the local system ID for lvmlocal mode.
|
|
||||||
# This is used when global/system_id_source is set to 'lvmlocal' in the
|
|
||||||
# main configuration file, e.g. lvm.conf. When used, it must be set to
|
|
||||||
# a unique value among all hosts sharing access to the storage,
|
|
||||||
# e.g. a host name.
|
|
||||||
#
|
|
||||||
# Example
|
|
||||||
# Set no system ID:
|
|
||||||
# system_id = ""
|
|
||||||
# Set the system_id to a specific name:
|
|
||||||
# system_id = "host1"
|
|
||||||
#
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# system_id = ""
|
|
||||||
|
|
||||||
# Configuration option local/extra_system_ids.
|
|
||||||
# A list of extra VG system IDs the local host can access.
|
|
||||||
# VGs with the system IDs listed here (in addition to the host's own
|
|
||||||
# system ID) can be fully accessed by the local host. (These are
|
|
||||||
# system IDs that the host sees in VGs, not system IDs that identify
|
|
||||||
# the local host, which is determined by system_id_source.)
|
|
||||||
# Use this only after consulting 'man lvmsystemid' to be certain of
|
|
||||||
# correct usage and possible dangers.
|
|
||||||
# This configuration option does not have a default value defined.
|
|
||||||
|
|
||||||
# Configuration option local/host_id.
|
|
||||||
# The lvmlockd sanlock host_id.
|
|
||||||
# This must be unique among all hosts, and must be between 1 and 2000.
|
|
||||||
# Applicable only if LVM is compiled with lockd support
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# host_id = 0
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# This is a metadata profile template for the LVM2 system.
|
|
||||||
#
|
|
||||||
# It contains all configuration settings that are customizable by metadata
|
|
||||||
# profiles. To create a new metadata profile, select the settings you want
|
|
||||||
# to customize and add them in a new file named <profile_name>.profile.
|
|
||||||
# Then install the new profile in a directory as defined by config/profile_dir
|
|
||||||
# setting found in @DEFAULT_SYS_DIR@/lvm.conf file.
|
|
||||||
#
|
|
||||||
# Metadata profiles can be referenced by using the --metadataprofile LVM2
|
|
||||||
# command line option.
|
|
||||||
#
|
|
||||||
# Refer to 'man lvm.conf' for further information about profiles and
|
|
||||||
# general configuration file layout.
|
|
||||||
#
|
|
||||||
allocation {
|
|
||||||
thin_pool_zero=1
|
|
||||||
thin_pool_discards="passdown"
|
|
||||||
thin_pool_chunk_size_policy="generic"
|
|
||||||
# thin_pool_chunk_size=128
|
|
||||||
}
|
|
||||||
activation {
|
|
||||||
thin_pool_autoextend_threshold=100
|
|
||||||
thin_pool_autoextend_percent=20
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
allocation {
|
|
||||||
thin_pool_chunk_size_policy = "generic"
|
|
||||||
thin_pool_zero = 1
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
allocation {
|
|
||||||
thin_pool_chunk_size_policy = "performance"
|
|
||||||
thin_pool_zero = 0
|
|
||||||
}
|
|
||||||
2373
configure.in
2373
configure.in
File diff suppressed because it is too large
Load Diff
@@ -1,146 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Coverity usage:
|
|
||||||
*
|
|
||||||
* translate model into xml
|
|
||||||
* cov-make-library -of coverity_model.xml coverity_model.c
|
|
||||||
*
|
|
||||||
* compile (using outdir 'cov'):
|
|
||||||
* cov-build --dir=cov make CC=gcc
|
|
||||||
*
|
|
||||||
* analyze (agressively, using 'cov')
|
|
||||||
* cov-analyze --dir cov --wait-for-license --hfa --concurrency --enable-fnptr --enable-constraint-fpp --security --all --aggressiveness-level=high --field-offset-escape --user-model-file=coverity/coverity_model.xml
|
|
||||||
*
|
|
||||||
* generate html output (to 'html' from 'cov'):
|
|
||||||
* cov-format-errors --dir cov --html-output html
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct lv_segment;
|
|
||||||
struct logical_volume;
|
|
||||||
|
|
||||||
struct lv_segment *first_seg(const struct logical_volume *lv)
|
|
||||||
{
|
|
||||||
return ((struct lv_segment **)lv)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
struct lv_segment *last_seg(const struct logical_volume *lv)
|
|
||||||
{
|
|
||||||
return ((struct lv_segment **)lv)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile)
|
|
||||||
{
|
|
||||||
return "STRING";
|
|
||||||
}
|
|
||||||
|
|
||||||
struct logical_volume *origin_from_cow(const struct logical_volume *lv)
|
|
||||||
{
|
|
||||||
if (lv)
|
|
||||||
return lv;
|
|
||||||
|
|
||||||
__coverity_panic__();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simple_memccpy() from glibc */
|
|
||||||
void *memccpy(void *dest, const void *src, int c, size_t n)
|
|
||||||
{
|
|
||||||
const char *s = src;
|
|
||||||
char *d = dest;
|
|
||||||
|
|
||||||
while (n-- > 0)
|
|
||||||
if ((*d++ = *s++) == (char) c)
|
|
||||||
return d;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 2 lines bellow needs to be placed in coverity/config/user_nodefs.h
|
|
||||||
* Not sure about any other way.
|
|
||||||
* Without them, coverity shows warning since x86 system header files
|
|
||||||
* are using inline assembly to reset fdset
|
|
||||||
*/
|
|
||||||
//#nodef FD_ZERO model_FD_ZERO
|
|
||||||
//void model_FD_ZERO(void *fdset);
|
|
||||||
|
|
||||||
void model_FD_ZERO(void *fdset)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = 0; i < 1024 / 8 / sizeof(long); ++i)
|
|
||||||
((long*)fdset)[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Resent Coverity reports quite weird errors... */
|
|
||||||
int *__errno_location(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
const unsigned short **__ctype_b_loc (void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Added extra pointer check to not need these models,
|
|
||||||
* for now just keep then in file
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
struct cmd_context;
|
|
||||||
struct profile;
|
|
||||||
|
|
||||||
const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile)
|
|
||||||
{
|
|
||||||
return "text";
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *find_config_tree_str_allow_empty(struct cmd_context *cmd, int id, struct profile *profile)
|
|
||||||
{
|
|
||||||
return "text";
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Until fixed coverity case# 00531860:
|
|
||||||
* A FORWARD_NULL false positive on a recursive function call
|
|
||||||
*
|
|
||||||
* model also these functions:
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
const struct dm_config_node;
|
|
||||||
const struct dm_config_node *find_config_tree_array(struct cmd_context *cmd, int id, struct profile *profile)
|
|
||||||
{
|
|
||||||
const struct dm_config_node *cn;
|
|
||||||
|
|
||||||
return cn;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct dm_config_node *find_config_tree_node(struct cmd_context *cmd, int id, struct profile *profile)
|
|
||||||
{
|
|
||||||
const struct dm_config_node *cn;
|
|
||||||
|
|
||||||
return cn;
|
|
||||||
}
|
|
||||||
|
|
||||||
int find_config_tree_bool(struct cmd_context *cmd, int id, struct profile *profile)
|
|
||||||
{
|
|
||||||
int b;
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
.PHONY: dmeventd clvmd cmirrord lvmetad lvmpolld lvmlockd
|
|
||||||
|
|
||||||
ifneq ("@CLVMD@", "none")
|
|
||||||
SUBDIRS += clvmd
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@BUILD_CMIRRORD@", "yes")
|
|
||||||
SUBDIRS += cmirrord
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@BUILD_DMEVENTD@", "yes")
|
|
||||||
SUBDIRS += dmeventd
|
|
||||||
ifneq ("$(CFLOW_CMD)", "")
|
|
||||||
daemons.cflow: dmeventd.cflow
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@BUILD_LVMETAD@", "yes")
|
|
||||||
SUBDIRS += lvmetad
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@BUILD_LVMPOLLD@", "yes")
|
|
||||||
SUBDIRS += lvmpolld
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@BUILD_LVMLOCKD@", "yes")
|
|
||||||
SUBDIRS += lvmlockd
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@BUILD_LVMDBUSD@", "yes")
|
|
||||||
SUBDIRS += lvmdbusd
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ("@BUILD_DMFILEMAPD@", "yes")
|
|
||||||
SUBDIRS += dmfilemapd
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(MAKECMDGOALS),distclean)
|
|
||||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd dmfilemapd
|
|
||||||
endif
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
ifeq ("@BUILD_DMEVENTD@", "yes")
|
|
||||||
device-mapper: dmeventd.device-mapper
|
|
||||||
endif
|
|
||||||
1
daemons/clvmd/.gitignore
vendored
1
daemons/clvmd/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
clvmd
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
CMAN_LIBS = @CMAN_LIBS@
|
|
||||||
CMAN_CFLAGS = @CMAN_CFLAGS@
|
|
||||||
CMAP_LIBS = @CMAP_LIBS@
|
|
||||||
CMAP_CFLAGS = @CMAP_CFLAGS@
|
|
||||||
CONFDB_LIBS = @CONFDB_LIBS@
|
|
||||||
CONFDB_CFLAGS = @CONFDB_CFLAGS@
|
|
||||||
CPG_LIBS = @CPG_LIBS@
|
|
||||||
CPG_CFLAGS = @CPG_CFLAGS@
|
|
||||||
DLM_LIBS = @DLM_LIBS@
|
|
||||||
DLM_CFLAGS = @DLM_CFLAGS@
|
|
||||||
QUORUM_LIBS = @QUORUM_LIBS@
|
|
||||||
QUORUM_CFLAGS = @QUORUM_CFLAGS@
|
|
||||||
SALCK_LIBS = @SALCK_LIBS@
|
|
||||||
SALCK_CFLAGS = @SALCK_CFLAGS@
|
|
||||||
|
|
||||||
SOURCES = \
|
|
||||||
clvmd-command.c\
|
|
||||||
clvmd.c\
|
|
||||||
lvm-functions.c\
|
|
||||||
refresh_clvmd.c
|
|
||||||
|
|
||||||
ifneq (,$(findstring cman,, "@CLVMD@,"))
|
|
||||||
SOURCES += clvmd-cman.c
|
|
||||||
LMLIBS += $(CMAN_LIBS) $(CONFDB_LIBS) $(DLM_LIBS)
|
|
||||||
CFLAGS += $(CMAN_CFLAGS) $(CONFDB_CFLAGS) $(DLM_CFLAGS)
|
|
||||||
DEFS += -DUSE_CMAN
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq (,$(findstring openais,, "@CLVMD@,"))
|
|
||||||
SOURCES += clvmd-openais.c
|
|
||||||
LMLIBS += $(CONFDB_LIBS) $(CPG_LIBS) $(SALCK_LIBS)
|
|
||||||
CFLAGS += $(CONFDB_CFLAGS) $(CPG_CFLAGS) $(SALCK_CFLAGS)
|
|
||||||
DEFS += -DUSE_OPENAIS
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq (,$(findstring corosync,, "@CLVMD@,"))
|
|
||||||
SOURCES += clvmd-corosync.c
|
|
||||||
LMLIBS += $(CMAP_LIBS) $(CONFDB_LIBS) $(CPG_LIBS) $(DLM_LIBS) $(QUORUM_LIBS)
|
|
||||||
CFLAGS += $(CMAP_CFLAGS) $(CONFDB_CFLAGS) $(CPG_CFLAGS) $(DLM_CFLAGS) $(QUORUM_CFLAGS)
|
|
||||||
DEFS += -DUSE_COROSYNC
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq (,$(findstring singlenode,, "@CLVMD@,"))
|
|
||||||
SOURCES += clvmd-singlenode.c
|
|
||||||
DEFS += -DUSE_SINGLENODE
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(MAKECMDGOALS),distclean)
|
|
||||||
SOURCES += clvmd-cman.c
|
|
||||||
SOURCES += clvmd-openais.c
|
|
||||||
SOURCES += clvmd-corosync.c
|
|
||||||
SOURCES += clvmd-singlenode.c
|
|
||||||
endif
|
|
||||||
|
|
||||||
TARGETS = \
|
|
||||||
clvmd
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS)
|
|
||||||
CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS)
|
|
||||||
|
|
||||||
INSTALL_TARGETS = \
|
|
||||||
install_clvmd
|
|
||||||
|
|
||||||
clvmd: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
|
|
||||||
-o clvmd $(OBJECTS) $(LMLIBS) $(LIBS)
|
|
||||||
|
|
||||||
.PHONY: install_clvmd
|
|
||||||
|
|
||||||
install_clvmd: $(TARGETS)
|
|
||||||
$(INSTALL_PROGRAM) -D clvmd $(usrsbindir)/clvmd
|
|
||||||
|
|
||||||
install: $(INSTALL_TARGETS)
|
|
||||||
|
|
||||||
install_cluster: $(INSTALL_TARGETS)
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
* Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Definitions for CLVMD server and clients */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The protocol spoken over the cluster and across the local socket.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _CLVM_H
|
|
||||||
#define _CLVM_H
|
|
||||||
|
|
||||||
#include "configure.h"
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
struct clvm_header {
|
|
||||||
uint8_t cmd; /* See below */
|
|
||||||
uint8_t flags; /* See below */
|
|
||||||
uint16_t xid; /* Transaction ID */
|
|
||||||
uint32_t clientid; /* Only used in Daemon->Daemon comms */
|
|
||||||
int32_t status; /* For replies, whether request succeeded */
|
|
||||||
uint32_t arglen; /* Length of argument below.
|
|
||||||
If >1500 then it will be passed
|
|
||||||
around the cluster in the system LV */
|
|
||||||
char node[1]; /* Actually a NUL-terminated string, node name.
|
|
||||||
If this is empty then the command is
|
|
||||||
forwarded to all cluster nodes unless
|
|
||||||
FLAG_LOCAL or FLAG_REMOTE is also set. */
|
|
||||||
char args[1]; /* Arguments for the command follow the
|
|
||||||
node name, This member is only
|
|
||||||
valid if the node name is empty */
|
|
||||||
} __attribute__ ((packed));
|
|
||||||
|
|
||||||
/* Flags */
|
|
||||||
#define CLVMD_FLAG_LOCAL 1 /* Only do this on the local node */
|
|
||||||
#define CLVMD_FLAG_SYSTEMLV 2 /* Data in system LV under my node name */
|
|
||||||
#define CLVMD_FLAG_NODEERRS 4 /* Reply has errors in node-specific portion */
|
|
||||||
#define CLVMD_FLAG_REMOTE 8 /* Do this on all nodes except for the local node */
|
|
||||||
|
|
||||||
/* Name of the local socket to communicate between lvm and clvmd */
|
|
||||||
#define CLVMD_SOCKNAME DEFAULT_RUN_DIR "/clvmd.sock"
|
|
||||||
|
|
||||||
/* Internal commands & replies */
|
|
||||||
#define CLVMD_CMD_REPLY 1
|
|
||||||
#define CLVMD_CMD_VERSION 2 /* Send version around cluster when we start */
|
|
||||||
#define CLVMD_CMD_GOAWAY 3 /* Die if received this - we are running
|
|
||||||
an incompatible version */
|
|
||||||
#define CLVMD_CMD_TEST 4 /* Just for mucking about */
|
|
||||||
|
|
||||||
#define CLVMD_CMD_LOCK 30
|
|
||||||
#define CLVMD_CMD_UNLOCK 31
|
|
||||||
|
|
||||||
/* Lock/Unlock commands */
|
|
||||||
#define CLVMD_CMD_LOCK_LV 50
|
|
||||||
#define CLVMD_CMD_LOCK_VG 51
|
|
||||||
#define CLVMD_CMD_LOCK_QUERY 52
|
|
||||||
|
|
||||||
/* Misc functions */
|
|
||||||
#define CLVMD_CMD_REFRESH 40
|
|
||||||
#define CLVMD_CMD_GET_CLUSTERNAME 41
|
|
||||||
#define CLVMD_CMD_SET_DEBUG 42
|
|
||||||
#define CLVMD_CMD_VG_BACKUP 43
|
|
||||||
#define CLVMD_CMD_RESTART 44
|
|
||||||
#define CLVMD_CMD_SYNC_NAMES 45
|
|
||||||
|
|
||||||
/* Used internally by some callers, but not part of the protocol.*/
|
|
||||||
#ifndef NODE_ALL
|
|
||||||
# define NODE_ALL "*"
|
|
||||||
# define NODE_LOCAL "."
|
|
||||||
# define NODE_REMOTE "^"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,505 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CMAN communication layer for clvmd.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "clvmd-common.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include "clvmd-comms.h"
|
|
||||||
#include "clvm.h"
|
|
||||||
#include "clvmd.h"
|
|
||||||
#include "lvm-functions.h"
|
|
||||||
|
|
||||||
#include <libdlm.h>
|
|
||||||
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
#define LOCKSPACE_NAME "clvmd"
|
|
||||||
|
|
||||||
struct clvmd_node
|
|
||||||
{
|
|
||||||
struct cman_node *node;
|
|
||||||
int clvmd_up;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int num_nodes;
|
|
||||||
static struct cman_node *nodes = NULL;
|
|
||||||
static struct cman_node this_node;
|
|
||||||
static int count_nodes; /* size of allocated nodes array */
|
|
||||||
static struct dm_hash_table *node_updown_hash;
|
|
||||||
static dlm_lshandle_t *lockspace;
|
|
||||||
static cman_handle_t c_handle;
|
|
||||||
|
|
||||||
static void count_clvmds_running(void);
|
|
||||||
static void get_members(void);
|
|
||||||
static int nodeid_from_csid(const char *csid);
|
|
||||||
static int name_from_nodeid(int nodeid, char *name);
|
|
||||||
static void event_callback(cman_handle_t handle, void *private, int reason, int arg);
|
|
||||||
static void data_callback(cman_handle_t handle, void *private,
|
|
||||||
char *buf, int len, uint8_t port, int nodeid);
|
|
||||||
|
|
||||||
struct lock_wait {
|
|
||||||
pthread_cond_t cond;
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
struct dlm_lksb lksb;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int _init_cluster(void)
|
|
||||||
{
|
|
||||||
node_updown_hash = dm_hash_create(100);
|
|
||||||
|
|
||||||
/* Open the cluster communication socket */
|
|
||||||
c_handle = cman_init(NULL);
|
|
||||||
if (!c_handle) {
|
|
||||||
syslog(LOG_ERR, "Can't open cluster manager socket: %m");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
DEBUGLOG("Connected to CMAN\n");
|
|
||||||
|
|
||||||
if (cman_start_recv_data(c_handle, data_callback, CLUSTER_PORT_CLVMD)) {
|
|
||||||
syslog(LOG_ERR, "Can't bind cluster socket: %m");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cman_start_notification(c_handle, event_callback)) {
|
|
||||||
syslog(LOG_ERR, "Can't start cluster event listening");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the cluster members list */
|
|
||||||
get_members();
|
|
||||||
count_clvmds_running();
|
|
||||||
|
|
||||||
DEBUGLOG("CMAN initialisation complete\n");
|
|
||||||
|
|
||||||
/* Create a lockspace for LV & VG locks to live in */
|
|
||||||
lockspace = dlm_open_lockspace(LOCKSPACE_NAME);
|
|
||||||
if (!lockspace) {
|
|
||||||
lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600);
|
|
||||||
if (!lockspace) {
|
|
||||||
syslog(LOG_ERR, "Unable to create DLM lockspace for CLVM: %m");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
DEBUGLOG("Created DLM lockspace for CLVMD.\n");
|
|
||||||
} else
|
|
||||||
DEBUGLOG("Opened existing DLM lockspace for CLVMD.\n");
|
|
||||||
|
|
||||||
dlm_ls_pthread_init(lockspace);
|
|
||||||
DEBUGLOG("DLM initialisation complete\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _cluster_init_completed(void)
|
|
||||||
{
|
|
||||||
clvmd_cluster_init_completed();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_main_cluster_fd(void)
|
|
||||||
{
|
|
||||||
return cman_get_fd(c_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_num_nodes(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int nnodes = 0;
|
|
||||||
|
|
||||||
/* return number of ACTIVE nodes */
|
|
||||||
for (i=0; i<num_nodes; i++) {
|
|
||||||
if (nodes[i].cn_member && nodes[i].cn_nodeid)
|
|
||||||
nnodes++;
|
|
||||||
}
|
|
||||||
return nnodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send_message with the fd check removed */
|
|
||||||
static int _cluster_send_message(const void *buf, int msglen, const char *csid,
|
|
||||||
const char *errtext)
|
|
||||||
{
|
|
||||||
int nodeid = 0;
|
|
||||||
|
|
||||||
if (csid)
|
|
||||||
memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN);
|
|
||||||
|
|
||||||
if (cman_send_data(c_handle, buf, msglen, 0, CLUSTER_PORT_CLVMD, nodeid) <= 0)
|
|
||||||
{
|
|
||||||
log_error("%s", errtext);
|
|
||||||
}
|
|
||||||
return msglen;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _get_our_csid(char *csid)
|
|
||||||
{
|
|
||||||
if (this_node.cn_nodeid == 0) {
|
|
||||||
cman_get_node(c_handle, 0, &this_node);
|
|
||||||
}
|
|
||||||
memcpy(csid, &this_node.cn_nodeid, CMAN_MAX_CSID_LEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call a callback routine for each node is that known (down means not running a clvmd) */
|
|
||||||
static int _cluster_do_node_callback(struct local_client *client,
|
|
||||||
void (*callback) (struct local_client *,
|
|
||||||
const char *,
|
|
||||||
int))
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int somedown = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < _get_num_nodes(); i++) {
|
|
||||||
if (nodes[i].cn_member && nodes[i].cn_nodeid) {
|
|
||||||
int up = (int)(long)dm_hash_lookup_binary(node_updown_hash, (char *)&nodes[i].cn_nodeid, sizeof(int));
|
|
||||||
|
|
||||||
callback(client, (char *)&nodes[i].cn_nodeid, up);
|
|
||||||
if (!up)
|
|
||||||
somedown = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return somedown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process OOB messages from the cluster socket */
|
|
||||||
static void event_callback(cman_handle_t handle, void *private, int reason, int arg)
|
|
||||||
{
|
|
||||||
char namebuf[MAX_CLUSTER_MEMBER_NAME_LEN];
|
|
||||||
|
|
||||||
switch (reason) {
|
|
||||||
case CMAN_REASON_PORTCLOSED:
|
|
||||||
name_from_nodeid(arg, namebuf);
|
|
||||||
log_notice("clvmd on node %s has died\n", namebuf);
|
|
||||||
DEBUGLOG("Got port closed message, removing node %s\n", namebuf);
|
|
||||||
|
|
||||||
dm_hash_insert_binary(node_updown_hash, (char *)&arg, sizeof(int), (void *)0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CMAN_REASON_STATECHANGE:
|
|
||||||
DEBUGLOG("Got state change message, re-reading members list\n");
|
|
||||||
get_members();
|
|
||||||
break;
|
|
||||||
|
|
||||||
#if defined(LIBCMAN_VERSION) && LIBCMAN_VERSION >= 2
|
|
||||||
case CMAN_REASON_PORTOPENED:
|
|
||||||
/* Ignore this, wait for startup message from clvmd itself */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CMAN_REASON_TRY_SHUTDOWN:
|
|
||||||
DEBUGLOG("Got try shutdown, sending OK\n");
|
|
||||||
cman_replyto_shutdown(c_handle, 1);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
/* ERROR */
|
|
||||||
DEBUGLOG("Got unknown event callback message: %d\n", reason);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct local_client *cman_client;
|
|
||||||
static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
|
|
||||||
const char *csid,
|
|
||||||
struct local_client **new_client)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Save this for data_callback */
|
|
||||||
cman_client = fd;
|
|
||||||
|
|
||||||
/* We never return a new client */
|
|
||||||
*new_client = NULL;
|
|
||||||
|
|
||||||
return cman_dispatch(c_handle, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void data_callback(cman_handle_t handle, void *private,
|
|
||||||
char *buf, int len, uint8_t port, int nodeid)
|
|
||||||
{
|
|
||||||
/* Ignore looped back messages */
|
|
||||||
if (nodeid == this_node.cn_nodeid)
|
|
||||||
return;
|
|
||||||
process_message(cman_client, buf, len, (char *)&nodeid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _add_up_node(const char *csid)
|
|
||||||
{
|
|
||||||
/* It's up ! */
|
|
||||||
int nodeid = nodeid_from_csid(csid);
|
|
||||||
|
|
||||||
dm_hash_insert_binary(node_updown_hash, (char *)&nodeid, sizeof(int), (void *)1);
|
|
||||||
DEBUGLOG("Added new node %d to updown list\n", nodeid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _cluster_closedown(void)
|
|
||||||
{
|
|
||||||
dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
|
|
||||||
cman_finish(c_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_listening(int nodeid)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
|
|
||||||
do {
|
|
||||||
status = cman_is_listening(c_handle, nodeid, CLUSTER_PORT_CLVMD);
|
|
||||||
if (status < 0 && errno == EBUSY) { /* Don't busywait */
|
|
||||||
sleep(1);
|
|
||||||
errno = EBUSY; /* In case sleep trashes it */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (status < 0 && errno == EBUSY);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Populate the list of CLVMDs running.
|
|
||||||
called only at startup time */
|
|
||||||
static void count_clvmds_running(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < num_nodes; i++) {
|
|
||||||
int nodeid = nodes[i].cn_nodeid;
|
|
||||||
|
|
||||||
if (is_listening(nodeid) == 1)
|
|
||||||
dm_hash_insert_binary(node_updown_hash, (void *)&nodeid, sizeof(int), (void*)1);
|
|
||||||
else
|
|
||||||
dm_hash_insert_binary(node_updown_hash, (void *)&nodeid, sizeof(int), (void*)0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get a list of active cluster members */
|
|
||||||
static void get_members(void)
|
|
||||||
{
|
|
||||||
int retnodes;
|
|
||||||
int status;
|
|
||||||
int i;
|
|
||||||
int high_nodeid = 0;
|
|
||||||
|
|
||||||
num_nodes = cman_get_node_count(c_handle);
|
|
||||||
if (num_nodes == -1) {
|
|
||||||
log_error("Unable to get node count");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Not enough room for new nodes list ? */
|
|
||||||
if (num_nodes > count_nodes && nodes) {
|
|
||||||
free(nodes);
|
|
||||||
nodes = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nodes == NULL) {
|
|
||||||
count_nodes = num_nodes + 10; /* Overallocate a little */
|
|
||||||
nodes = malloc(count_nodes * sizeof(struct cman_node));
|
|
||||||
if (!nodes) {
|
|
||||||
log_error("Unable to allocate nodes array\n");
|
|
||||||
exit(5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status = cman_get_nodes(c_handle, count_nodes, &retnodes, nodes);
|
|
||||||
if (status < 0) {
|
|
||||||
log_error("Unable to get node details");
|
|
||||||
exit(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the highest nodeid */
|
|
||||||
for (i=0; i<retnodes; i++) {
|
|
||||||
if (nodes[i].cn_nodeid > high_nodeid)
|
|
||||||
high_nodeid = nodes[i].cn_nodeid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Convert a node name to a CSID */
|
|
||||||
static int _csid_from_name(char *csid, const char *name)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < num_nodes; i++) {
|
|
||||||
if (strcmp(name, nodes[i].cn_name) == 0) {
|
|
||||||
memcpy(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert a CSID to a node name */
|
|
||||||
static int _name_from_csid(const char *csid, char *name)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < num_nodes; i++) {
|
|
||||||
if (memcmp(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN) == 0) {
|
|
||||||
strcpy(name, nodes[i].cn_name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Who?? */
|
|
||||||
strcpy(name, "Unknown");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert a node ID to a node name */
|
|
||||||
static int name_from_nodeid(int nodeid, char *name)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < num_nodes; i++) {
|
|
||||||
if (nodeid == nodes[i].cn_nodeid) {
|
|
||||||
strcpy(name, nodes[i].cn_name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Who?? */
|
|
||||||
strcpy(name, "Unknown");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert a CSID to a node ID */
|
|
||||||
static int nodeid_from_csid(const char *csid)
|
|
||||||
{
|
|
||||||
int nodeid;
|
|
||||||
|
|
||||||
memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN);
|
|
||||||
|
|
||||||
return nodeid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _is_quorate(void)
|
|
||||||
{
|
|
||||||
return cman_is_quorate(c_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sync_ast_routine(void *arg)
|
|
||||||
{
|
|
||||||
struct lock_wait *lwait = arg;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lwait->mutex);
|
|
||||||
pthread_cond_signal(&lwait->cond);
|
|
||||||
pthread_mutex_unlock(&lwait->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
struct lock_wait lwait;
|
|
||||||
|
|
||||||
if (!lockid) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGLOG("sync_lock: '%s' mode:%d flags=%d\n", resource,mode,flags);
|
|
||||||
/* Conversions need the lockid in the LKSB */
|
|
||||||
if (flags & LKF_CONVERT)
|
|
||||||
lwait.lksb.sb_lkid = *lockid;
|
|
||||||
|
|
||||||
pthread_cond_init(&lwait.cond, NULL);
|
|
||||||
pthread_mutex_init(&lwait.mutex, NULL);
|
|
||||||
pthread_mutex_lock(&lwait.mutex);
|
|
||||||
|
|
||||||
status = dlm_ls_lock(lockspace,
|
|
||||||
mode,
|
|
||||||
&lwait.lksb,
|
|
||||||
flags,
|
|
||||||
resource,
|
|
||||||
strlen(resource),
|
|
||||||
0, sync_ast_routine, &lwait, NULL, NULL);
|
|
||||||
if (status)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
/* Wait for it to complete */
|
|
||||||
pthread_cond_wait(&lwait.cond, &lwait.mutex);
|
|
||||||
pthread_mutex_unlock(&lwait.mutex);
|
|
||||||
|
|
||||||
*lockid = lwait.lksb.sb_lkid;
|
|
||||||
|
|
||||||
errno = lwait.lksb.sb_status;
|
|
||||||
DEBUGLOG("sync_lock: returning lkid %x\n", *lockid);
|
|
||||||
if (lwait.lksb.sb_status)
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _sync_unlock(const char *resource /* UNUSED */, int lockid)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
struct lock_wait lwait;
|
|
||||||
|
|
||||||
DEBUGLOG("sync_unlock: '%s' lkid:%x\n", resource, lockid);
|
|
||||||
|
|
||||||
pthread_cond_init(&lwait.cond, NULL);
|
|
||||||
pthread_mutex_init(&lwait.mutex, NULL);
|
|
||||||
pthread_mutex_lock(&lwait.mutex);
|
|
||||||
|
|
||||||
status = dlm_ls_unlock(lockspace, lockid, 0, &lwait.lksb, &lwait);
|
|
||||||
|
|
||||||
if (status)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
/* Wait for it to complete */
|
|
||||||
pthread_cond_wait(&lwait.cond, &lwait.mutex);
|
|
||||||
pthread_mutex_unlock(&lwait.mutex);
|
|
||||||
|
|
||||||
errno = lwait.lksb.sb_status;
|
|
||||||
if (lwait.lksb.sb_status != EUNLOCK)
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_cluster_name(char *buf, int buflen)
|
|
||||||
{
|
|
||||||
cman_cluster_t cluster_info;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
status = cman_get_cluster(c_handle, &cluster_info);
|
|
||||||
if (!status) {
|
|
||||||
strncpy(buf, cluster_info.ci_name, buflen);
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct cluster_ops _cluster_cman_ops = {
|
|
||||||
.name = "cman",
|
|
||||||
.cluster_init_completed = _cluster_init_completed,
|
|
||||||
.cluster_send_message = _cluster_send_message,
|
|
||||||
.name_from_csid = _name_from_csid,
|
|
||||||
.csid_from_name = _csid_from_name,
|
|
||||||
.get_num_nodes = _get_num_nodes,
|
|
||||||
.cluster_fd_callback = _cluster_fd_callback,
|
|
||||||
.get_main_cluster_fd = _get_main_cluster_fd,
|
|
||||||
.cluster_do_node_callback = _cluster_do_node_callback,
|
|
||||||
.is_quorate = _is_quorate,
|
|
||||||
.get_our_csid = _get_our_csid,
|
|
||||||
.add_up_node = _add_up_node,
|
|
||||||
.cluster_closedown = _cluster_closedown,
|
|
||||||
.get_cluster_name = _get_cluster_name,
|
|
||||||
.sync_lock = _sync_lock,
|
|
||||||
.sync_unlock = _sync_unlock,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cluster_ops *init_cman_cluster(void)
|
|
||||||
{
|
|
||||||
if (!_init_cluster())
|
|
||||||
return &_cluster_cman_ops;
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
@@ -1,414 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
* Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
CLVMD Cluster LVM daemon command processor.
|
|
||||||
|
|
||||||
To add commands to the daemon simply add a processor in do_command and return
|
|
||||||
and messages back in buf and the length in *retlen. The initial value of
|
|
||||||
buflen is the maximum size of the buffer. if buf is not large enough then it
|
|
||||||
may be reallocated by the functions in here to a suitable size bearing in
|
|
||||||
mind that anything larger than the passed-in size will have to be returned
|
|
||||||
using the system LV and so performance will suffer.
|
|
||||||
|
|
||||||
The status return will be negated and passed back to the originating node.
|
|
||||||
|
|
||||||
pre- and post- command routines are called only on the local node. The
|
|
||||||
purpose is primarily to get and release locks, though the pre- routine should
|
|
||||||
also do any other local setups required by the command (if any) and can
|
|
||||||
return a failure code that prevents the command from being distributed around
|
|
||||||
the cluster
|
|
||||||
|
|
||||||
The pre- and post- routines are run in their own thread so can block as long
|
|
||||||
they like, do_command is run in the main clvmd thread so should not block for
|
|
||||||
too long. If the pre-command returns an error code (!=0) then the command
|
|
||||||
will not be propogated around the cluster but the post-command WILL be called
|
|
||||||
|
|
||||||
Also note that the pre and post routine are *always* called on the local
|
|
||||||
node, even if the command to be executed was only requested to run on a
|
|
||||||
remote node. It may peek inside the client structure to check the status of
|
|
||||||
the command.
|
|
||||||
|
|
||||||
The clients of the daemon must, naturally, understand the return messages and
|
|
||||||
codes.
|
|
||||||
|
|
||||||
Routines in here may only READ the values in the client structure passed in
|
|
||||||
apart from client->private which they are free to do what they like with.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "clvmd-common.h"
|
|
||||||
#include "clvmd-comms.h"
|
|
||||||
#include "clvm.h"
|
|
||||||
#include "clvmd.h"
|
|
||||||
#include "lvm-globals.h"
|
|
||||||
#include "lvm-functions.h"
|
|
||||||
|
|
||||||
#include "locking.h"
|
|
||||||
|
|
||||||
#include <sys/utsname.h>
|
|
||||||
|
|
||||||
extern struct cluster_ops *clops;
|
|
||||||
static int restart_clvmd(void);
|
|
||||||
|
|
||||||
/* This is where all the real work happens:
|
|
||||||
NOTE: client will be NULL when this is executed on a remote node */
|
|
||||||
int do_command(struct local_client *client, struct clvm_header *msg, int msglen,
|
|
||||||
char **buf, int buflen, int *retlen)
|
|
||||||
{
|
|
||||||
char *args = msg->node + strlen(msg->node) + 1;
|
|
||||||
int arglen = msglen - sizeof(struct clvm_header) - strlen(msg->node);
|
|
||||||
int status = 0;
|
|
||||||
char *lockname;
|
|
||||||
const char *locktype;
|
|
||||||
struct utsname nodeinfo;
|
|
||||||
unsigned char lock_cmd;
|
|
||||||
unsigned char lock_flags;
|
|
||||||
|
|
||||||
/* Do the command */
|
|
||||||
switch (msg->cmd) {
|
|
||||||
/* Just a test message */
|
|
||||||
case CLVMD_CMD_TEST:
|
|
||||||
if (arglen > buflen) {
|
|
||||||
char *new_buf;
|
|
||||||
buflen = arglen + 200;
|
|
||||||
new_buf = realloc(*buf, buflen);
|
|
||||||
if (new_buf == NULL) {
|
|
||||||
status = errno;
|
|
||||||
free (*buf);
|
|
||||||
}
|
|
||||||
*buf = new_buf;
|
|
||||||
}
|
|
||||||
if (*buf) {
|
|
||||||
if (uname(&nodeinfo))
|
|
||||||
memset(&nodeinfo, 0, sizeof(nodeinfo));
|
|
||||||
|
|
||||||
*retlen = 1 + dm_snprintf(*buf, buflen,
|
|
||||||
"TEST from %s: %s v%s",
|
|
||||||
nodeinfo.nodename, args,
|
|
||||||
nodeinfo.release);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_LOCK_VG:
|
|
||||||
lock_cmd = args[0];
|
|
||||||
lock_flags = args[1];
|
|
||||||
lockname = &args[2];
|
|
||||||
/* Check to see if the VG is in use by LVM1 */
|
|
||||||
status = do_check_lvm1(lockname);
|
|
||||||
do_lock_vg(lock_cmd, lock_flags, lockname);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_LOCK_LV:
|
|
||||||
/* This is the biggie */
|
|
||||||
lock_cmd = args[0];
|
|
||||||
lock_flags = args[1];
|
|
||||||
lockname = &args[2];
|
|
||||||
status = do_lock_lv(lock_cmd, lock_flags, lockname);
|
|
||||||
/* Replace EIO with something less scary */
|
|
||||||
if (status == EIO) {
|
|
||||||
*retlen = 1 + dm_snprintf(*buf, buflen, "%s",
|
|
||||||
get_last_lvm_error());
|
|
||||||
return EIO;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_LOCK_QUERY:
|
|
||||||
lockname = &args[2];
|
|
||||||
if (buflen < 3)
|
|
||||||
return EIO;
|
|
||||||
if ((locktype = do_lock_query(lockname)))
|
|
||||||
*retlen = 1 + dm_snprintf(*buf, buflen, "%s", locktype);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_REFRESH:
|
|
||||||
do_refresh_cache();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_SYNC_NAMES:
|
|
||||||
lvm_do_fs_unlock();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_SET_DEBUG:
|
|
||||||
clvmd_set_debug((debug_t) args[0]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_RESTART:
|
|
||||||
status = restart_clvmd();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_GET_CLUSTERNAME:
|
|
||||||
status = clops->get_cluster_name(*buf, buflen);
|
|
||||||
if (!status)
|
|
||||||
*retlen = strlen(*buf)+1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_VG_BACKUP:
|
|
||||||
/*
|
|
||||||
* Do not run backup on local node, caller should do that.
|
|
||||||
*/
|
|
||||||
if (!client)
|
|
||||||
lvm_do_backup(&args[2]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Won't get here because command is validated in pre_command */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check the status of the command and return the error text */
|
|
||||||
if (status) {
|
|
||||||
*retlen = 1 + ((*buf) ? dm_snprintf(*buf, buflen, "%s",
|
|
||||||
strerror(status)) : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lock_vg(struct local_client *client)
|
|
||||||
{
|
|
||||||
struct dm_hash_table *lock_hash;
|
|
||||||
struct clvm_header *header =
|
|
||||||
(struct clvm_header *) client->bits.localsock.cmd;
|
|
||||||
unsigned char lock_cmd;
|
|
||||||
int lock_mode;
|
|
||||||
char *args = header->node + strlen(header->node) + 1;
|
|
||||||
int lkid;
|
|
||||||
int status;
|
|
||||||
char *lockname;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Keep a track of VG locks in our own hash table. In current
|
|
||||||
* practice there should only ever be more than two VGs locked
|
|
||||||
* if a user tries to merge lots of them at once
|
|
||||||
*/
|
|
||||||
if (!client->bits.localsock.private) {
|
|
||||||
if (!(lock_hash = dm_hash_create(3)))
|
|
||||||
return ENOMEM;
|
|
||||||
client->bits.localsock.private = (void *) lock_hash;
|
|
||||||
} else
|
|
||||||
lock_hash = (struct dm_hash_table *) client->bits.localsock.private;
|
|
||||||
|
|
||||||
lock_cmd = args[0] & (LCK_NONBLOCK | LCK_HOLD | LCK_SCOPE_MASK | LCK_TYPE_MASK);
|
|
||||||
lock_mode = ((int) lock_cmd & LCK_TYPE_MASK);
|
|
||||||
/* lock_flags = args[1]; */
|
|
||||||
lockname = &args[2];
|
|
||||||
DEBUGLOG("(%p) doing PRE command LOCK_VG '%s' at %x\n", client, lockname, lock_cmd);
|
|
||||||
|
|
||||||
if (lock_mode == LCK_UNLOCK) {
|
|
||||||
if (!(lkid = (int) (long) dm_hash_lookup(lock_hash, lockname)))
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
if ((status = sync_unlock(lockname, lkid)))
|
|
||||||
status = errno;
|
|
||||||
else
|
|
||||||
dm_hash_remove(lock_hash, lockname);
|
|
||||||
} else {
|
|
||||||
/* Read locks need to be PR; other modes get passed through */
|
|
||||||
if (lock_mode == LCK_READ)
|
|
||||||
lock_mode = LCK_PREAD;
|
|
||||||
|
|
||||||
if ((status = sync_lock(lockname, lock_mode, (lock_cmd & LCK_NONBLOCK) ? LCKF_NOQUEUE : 0, &lkid)))
|
|
||||||
status = errno;
|
|
||||||
else if (!dm_hash_insert(lock_hash, lockname, (void *) (long) lkid))
|
|
||||||
return ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Pre-command is a good place to get locks that are needed only for the duration
|
|
||||||
of the commands around the cluster (don't forget to free them in post-command),
|
|
||||||
and to sanity check the command arguments */
|
|
||||||
int do_pre_command(struct local_client *client)
|
|
||||||
{
|
|
||||||
struct clvm_header *header =
|
|
||||||
(struct clvm_header *) client->bits.localsock.cmd;
|
|
||||||
unsigned char lock_cmd;
|
|
||||||
unsigned char lock_flags;
|
|
||||||
char *args = header->node + strlen(header->node) + 1;
|
|
||||||
int lockid = 0;
|
|
||||||
int status = 0;
|
|
||||||
char *lockname;
|
|
||||||
|
|
||||||
switch (header->cmd) {
|
|
||||||
case CLVMD_CMD_TEST:
|
|
||||||
status = sync_lock("CLVMD_TEST", LCK_EXCL, 0, &lockid);
|
|
||||||
client->bits.localsock.private = (void *)(long)lockid;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_LOCK_VG:
|
|
||||||
lockname = &args[2];
|
|
||||||
/* We take out a real lock unless LCK_CACHE was set */
|
|
||||||
if (!strncmp(lockname, "V_", 2) ||
|
|
||||||
!strncmp(lockname, "P_#", 3))
|
|
||||||
status = lock_vg(client);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_LOCK_LV:
|
|
||||||
lock_cmd = args[0];
|
|
||||||
lock_flags = args[1];
|
|
||||||
lockname = &args[2];
|
|
||||||
status = pre_lock_lv(lock_cmd, lock_flags, lockname);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_REFRESH:
|
|
||||||
case CLVMD_CMD_GET_CLUSTERNAME:
|
|
||||||
case CLVMD_CMD_SET_DEBUG:
|
|
||||||
case CLVMD_CMD_VG_BACKUP:
|
|
||||||
case CLVMD_CMD_SYNC_NAMES:
|
|
||||||
case CLVMD_CMD_LOCK_QUERY:
|
|
||||||
case CLVMD_CMD_RESTART:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
log_error("Unknown command %d received\n", header->cmd);
|
|
||||||
status = EINVAL;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Note that the post-command routine is called even if the pre-command or the real command
|
|
||||||
failed */
|
|
||||||
int do_post_command(struct local_client *client)
|
|
||||||
{
|
|
||||||
struct clvm_header *header =
|
|
||||||
(struct clvm_header *) client->bits.localsock.cmd;
|
|
||||||
int status = 0;
|
|
||||||
unsigned char lock_cmd;
|
|
||||||
unsigned char lock_flags;
|
|
||||||
char *args = header->node + strlen(header->node) + 1;
|
|
||||||
char *lockname;
|
|
||||||
|
|
||||||
switch (header->cmd) {
|
|
||||||
case CLVMD_CMD_TEST:
|
|
||||||
status = sync_unlock("CLVMD_TEST", (int) (long) client->bits.localsock.private);
|
|
||||||
client->bits.localsock.private = NULL;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLVMD_CMD_LOCK_LV:
|
|
||||||
lock_cmd = args[0];
|
|
||||||
lock_flags = args[1];
|
|
||||||
lockname = &args[2];
|
|
||||||
status = post_lock_lv(lock_cmd, lock_flags, lockname);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Nothing to do here */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Called when the client is about to be deleted */
|
|
||||||
void cmd_client_cleanup(struct local_client *client)
|
|
||||||
{
|
|
||||||
struct dm_hash_node *v;
|
|
||||||
struct dm_hash_table *lock_hash;
|
|
||||||
int lkid;
|
|
||||||
char *lockname;
|
|
||||||
|
|
||||||
DEBUGLOG("(%p) Client thread cleanup\n", client);
|
|
||||||
if (!client->bits.localsock.private)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_hash = (struct dm_hash_table *)client->bits.localsock.private;
|
|
||||||
|
|
||||||
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("(%p) Cleanup: Unlocking lock %s %x\n", client, lockname, lkid);
|
|
||||||
(void) sync_unlock(lockname, lkid);
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_hash_destroy(lock_hash);
|
|
||||||
client->bits.localsock.private = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int restart_clvmd(void)
|
|
||||||
{
|
|
||||||
const char **argv;
|
|
||||||
char *lv_name;
|
|
||||||
int argc = 0, max_locks = 0;
|
|
||||||
struct dm_hash_node *hn = NULL;
|
|
||||||
char debug_arg[16];
|
|
||||||
const char *clvmd = getenv("LVM_CLVMD_BINARY") ? : CLVMD_PATH;
|
|
||||||
|
|
||||||
DEBUGLOG("clvmd restart requested\n");
|
|
||||||
|
|
||||||
/* Count exclusively-open LVs */
|
|
||||||
do {
|
|
||||||
hn = get_next_excl_lock(hn, &lv_name);
|
|
||||||
if (lv_name) {
|
|
||||||
max_locks++;
|
|
||||||
if (!*lv_name)
|
|
||||||
break; /* FIXME: Is this error ? */
|
|
||||||
}
|
|
||||||
} while (hn);
|
|
||||||
|
|
||||||
/* clvmd + locks (-E uuid) + debug (-d X) + NULL */
|
|
||||||
if (!(argv = malloc((max_locks * 2 + 6) * sizeof(*argv))))
|
|
||||||
goto_out;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Build the command-line
|
|
||||||
*/
|
|
||||||
argv[argc++] = "clvmd";
|
|
||||||
|
|
||||||
/* Propagate debug options */
|
|
||||||
if (clvmd_get_debug()) {
|
|
||||||
if (dm_snprintf(debug_arg, sizeof(debug_arg), "-d%u", clvmd_get_debug()) < 0)
|
|
||||||
goto_out;
|
|
||||||
argv[argc++] = debug_arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Propagate foreground options */
|
|
||||||
if (clvmd_get_foreground())
|
|
||||||
argv[argc++] = "-f";
|
|
||||||
|
|
||||||
argv[argc++] = "-I";
|
|
||||||
argv[argc++] = clops->name;
|
|
||||||
|
|
||||||
/* Now add the exclusively-open LVs */
|
|
||||||
hn = NULL;
|
|
||||||
do {
|
|
||||||
hn = get_next_excl_lock(hn, &lv_name);
|
|
||||||
if (lv_name) {
|
|
||||||
if (!*lv_name)
|
|
||||||
break; /* FIXME: Is this error ? */
|
|
||||||
argv[argc++] = "-E";
|
|
||||||
argv[argc++] = lv_name;
|
|
||||||
DEBUGLOG("excl lock: %s\n", lv_name);
|
|
||||||
}
|
|
||||||
} while (hn);
|
|
||||||
argv[argc] = NULL;
|
|
||||||
|
|
||||||
/* Exec new clvmd */
|
|
||||||
DEBUGLOG("--- Restarting %s ---\n", clvmd);
|
|
||||||
for (argc = 1; argv[argc]; argc++) DEBUGLOG("--- %d: %s\n", argc, argv[argc]);
|
|
||||||
|
|
||||||
/* NOTE: This will fail when downgrading! */
|
|
||||||
execvp(clvmd, (char **)argv);
|
|
||||||
out:
|
|
||||||
/* We failed */
|
|
||||||
DEBUGLOG("Restart of clvmd failed.\n");
|
|
||||||
|
|
||||||
free(argv);
|
|
||||||
|
|
||||||
return EIO;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file must be included first by every clvmd source file.
|
|
||||||
*/
|
|
||||||
#ifndef _LVM_CLVMD_COMMON_H
|
|
||||||
#define _LVM_CLVMD_COMMON_H
|
|
||||||
|
|
||||||
#define _REENTRANT
|
|
||||||
|
|
||||||
#include "tool.h"
|
|
||||||
|
|
||||||
#include "lvm-logging.h"
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
* Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Abstraction layer for clvmd cluster communications
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _CLVMD_COMMS_H
|
|
||||||
#define _CLVMD_COMMS_H
|
|
||||||
|
|
||||||
struct local_client;
|
|
||||||
|
|
||||||
struct cluster_ops {
|
|
||||||
const char *name;
|
|
||||||
void (*cluster_init_completed) (void);
|
|
||||||
|
|
||||||
int (*cluster_send_message) (const void *buf, int msglen,
|
|
||||||
const char *csid,
|
|
||||||
const char *errtext);
|
|
||||||
int (*name_from_csid) (const char *csid, char *name);
|
|
||||||
int (*csid_from_name) (char *csid, const char *name);
|
|
||||||
int (*get_num_nodes) (void);
|
|
||||||
int (*cluster_fd_callback) (struct local_client *fd, char *buf, int len,
|
|
||||||
const char *csid,
|
|
||||||
struct local_client **new_client);
|
|
||||||
int (*get_main_cluster_fd) (void); /* gets accept FD or cman cluster socket */
|
|
||||||
int (*cluster_do_node_callback) (struct local_client *client,
|
|
||||||
void (*callback) (struct local_client *,
|
|
||||||
const char *csid,
|
|
||||||
int node_up));
|
|
||||||
int (*is_quorate) (void);
|
|
||||||
|
|
||||||
void (*get_our_csid) (char *csid);
|
|
||||||
void (*add_up_node) (const char *csid);
|
|
||||||
void (*reread_config) (void);
|
|
||||||
void (*cluster_closedown) (void);
|
|
||||||
|
|
||||||
int (*get_cluster_name)(char *buf, int buflen);
|
|
||||||
|
|
||||||
int (*sync_lock) (const char *resource, int mode,
|
|
||||||
int flags, int *lockid);
|
|
||||||
int (*sync_unlock) (const char *resource, int lockid);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef USE_CMAN
|
|
||||||
# include <netinet/in.h>
|
|
||||||
# include "libcman.h"
|
|
||||||
# define CMAN_MAX_CSID_LEN 4
|
|
||||||
# ifndef MAX_CSID_LEN
|
|
||||||
# define MAX_CSID_LEN CMAN_MAX_CSID_LEN
|
|
||||||
# endif
|
|
||||||
# undef MAX_CLUSTER_MEMBER_NAME_LEN
|
|
||||||
# define MAX_CLUSTER_MEMBER_NAME_LEN CMAN_MAX_NODENAME_LEN
|
|
||||||
# define CMAN_MAX_CLUSTER_MESSAGE 1500
|
|
||||||
# define CLUSTER_PORT_CLVMD 11
|
|
||||||
struct cluster_ops *init_cman_cluster(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_OPENAIS
|
|
||||||
# include <openais/saAis.h>
|
|
||||||
# include <corosync/totem/totem.h>
|
|
||||||
# define OPENAIS_CSID_LEN (sizeof(int))
|
|
||||||
# define OPENAIS_MAX_CLUSTER_MESSAGE MESSAGE_SIZE_MAX
|
|
||||||
# define OPENAIS_MAX_CLUSTER_MEMBER_NAME_LEN SA_MAX_NAME_LENGTH
|
|
||||||
# ifndef MAX_CLUSTER_MEMBER_NAME_LEN
|
|
||||||
# define MAX_CLUSTER_MEMBER_NAME_LEN SA_MAX_NAME_LENGTH
|
|
||||||
# endif
|
|
||||||
# ifndef CMAN_MAX_CLUSTER_MESSAGE
|
|
||||||
# define CMAN_MAX_CLUSTER_MESSAGE MESSAGE_SIZE_MAX
|
|
||||||
# endif
|
|
||||||
# ifndef MAX_CSID_LEN
|
|
||||||
# define MAX_CSID_LEN sizeof(int)
|
|
||||||
# endif
|
|
||||||
struct cluster_ops *init_openais_cluster(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_COROSYNC
|
|
||||||
# include <corosync/corotypes.h>
|
|
||||||
# define COROSYNC_CSID_LEN (sizeof(int))
|
|
||||||
# define COROSYNC_MAX_CLUSTER_MESSAGE 65535
|
|
||||||
# define COROSYNC_MAX_CLUSTER_MEMBER_NAME_LEN CS_MAX_NAME_LENGTH
|
|
||||||
# ifndef MAX_CLUSTER_MEMBER_NAME_LEN
|
|
||||||
# define MAX_CLUSTER_MEMBER_NAME_LEN CS_MAX_NAME_LENGTH
|
|
||||||
# endif
|
|
||||||
# ifndef CMAN_MAX_CLUSTER_MESSAGE
|
|
||||||
# define CMAN_MAX_CLUSTER_MESSAGE 65535
|
|
||||||
# endif
|
|
||||||
# ifndef MAX_CSID_LEN
|
|
||||||
# define MAX_CSID_LEN sizeof(int)
|
|
||||||
# endif
|
|
||||||
struct cluster_ops *init_corosync_cluster(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_SINGLENODE
|
|
||||||
# define SINGLENODE_CSID_LEN (sizeof(int))
|
|
||||||
# ifndef MAX_CLUSTER_MEMBER_NAME_LEN
|
|
||||||
# define MAX_CLUSTER_MEMBER_NAME_LEN 64
|
|
||||||
# endif
|
|
||||||
# define SINGLENODE_MAX_CLUSTER_MESSAGE 65535
|
|
||||||
# ifndef MAX_CSID_LEN
|
|
||||||
# define MAX_CSID_LEN sizeof(int)
|
|
||||||
# endif
|
|
||||||
struct cluster_ops *init_singlenode_cluster(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,662 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009-2012 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This provides the interface between clvmd and corosync/DLM as the cluster
|
|
||||||
* and lock manager.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "clvmd-common.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include "clvm.h"
|
|
||||||
#include "clvmd-comms.h"
|
|
||||||
#include "clvmd.h"
|
|
||||||
#include "lvm-functions.h"
|
|
||||||
|
|
||||||
#include "locking.h"
|
|
||||||
|
|
||||||
#include <corosync/cpg.h>
|
|
||||||
#include <corosync/quorum.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_COROSYNC_CONFDB_H
|
|
||||||
# include <corosync/confdb.h>
|
|
||||||
#elif defined HAVE_COROSYNC_CMAP_H
|
|
||||||
# include <corosync/cmap.h>
|
|
||||||
#else
|
|
||||||
# error "Either HAVE_COROSYNC_CONFDB_H or HAVE_COROSYNC_CMAP_H must be defined."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libdlm.h>
|
|
||||||
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
/* Timeout value for several corosync calls */
|
|
||||||
#define LOCKSPACE_NAME "clvmd"
|
|
||||||
|
|
||||||
static void corosync_cpg_deliver_callback (cpg_handle_t handle,
|
|
||||||
const struct cpg_name *groupName,
|
|
||||||
uint32_t nodeid,
|
|
||||||
uint32_t pid,
|
|
||||||
void *msg,
|
|
||||||
size_t msg_len);
|
|
||||||
static void corosync_cpg_confchg_callback(cpg_handle_t handle,
|
|
||||||
const struct cpg_name *groupName,
|
|
||||||
const struct cpg_address *member_list, size_t member_list_entries,
|
|
||||||
const struct cpg_address *left_list, size_t left_list_entries,
|
|
||||||
const struct cpg_address *joined_list, size_t joined_list_entries);
|
|
||||||
static void _cluster_closedown(void);
|
|
||||||
|
|
||||||
/* Hash list of nodes in the cluster */
|
|
||||||
static struct dm_hash_table *node_hash;
|
|
||||||
|
|
||||||
/* Number of active nodes */
|
|
||||||
static int num_nodes;
|
|
||||||
static unsigned int our_nodeid;
|
|
||||||
|
|
||||||
static struct local_client *cluster_client;
|
|
||||||
|
|
||||||
/* Corosync handles */
|
|
||||||
static cpg_handle_t cpg_handle;
|
|
||||||
static quorum_handle_t quorum_handle;
|
|
||||||
|
|
||||||
/* DLM Handle */
|
|
||||||
static dlm_lshandle_t *lockspace;
|
|
||||||
|
|
||||||
static struct cpg_name cpg_group_name;
|
|
||||||
|
|
||||||
/* Corosync callback structs */
|
|
||||||
cpg_callbacks_t corosync_cpg_callbacks = {
|
|
||||||
.cpg_deliver_fn = corosync_cpg_deliver_callback,
|
|
||||||
.cpg_confchg_fn = corosync_cpg_confchg_callback,
|
|
||||||
};
|
|
||||||
|
|
||||||
quorum_callbacks_t quorum_callbacks = {
|
|
||||||
.quorum_notify_fn = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct node_info
|
|
||||||
{
|
|
||||||
enum {NODE_DOWN, NODE_CLVMD} state;
|
|
||||||
int nodeid;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Set errno to something approximating the right value and return 0 or -1 */
|
|
||||||
static int cs_to_errno(cs_error_t err)
|
|
||||||
{
|
|
||||||
switch(err)
|
|
||||||
{
|
|
||||||
case CS_OK:
|
|
||||||
return 0;
|
|
||||||
case CS_ERR_LIBRARY:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case CS_ERR_VERSION:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case CS_ERR_INIT:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case CS_ERR_TIMEOUT:
|
|
||||||
errno = ETIME;
|
|
||||||
break;
|
|
||||||
case CS_ERR_TRY_AGAIN:
|
|
||||||
errno = EAGAIN;
|
|
||||||
break;
|
|
||||||
case CS_ERR_INVALID_PARAM:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case CS_ERR_NO_MEMORY:
|
|
||||||
errno = ENOMEM;
|
|
||||||
break;
|
|
||||||
case CS_ERR_BAD_HANDLE:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case CS_ERR_BUSY:
|
|
||||||
errno = EBUSY;
|
|
||||||
break;
|
|
||||||
case CS_ERR_ACCESS:
|
|
||||||
errno = EPERM;
|
|
||||||
break;
|
|
||||||
case CS_ERR_NOT_EXIST:
|
|
||||||
errno = ENOENT;
|
|
||||||
break;
|
|
||||||
case CS_ERR_NAME_TOO_LONG:
|
|
||||||
errno = ENAMETOOLONG;
|
|
||||||
break;
|
|
||||||
case CS_ERR_EXIST:
|
|
||||||
errno = EEXIST;
|
|
||||||
break;
|
|
||||||
case CS_ERR_NO_SPACE:
|
|
||||||
errno = ENOSPC;
|
|
||||||
break;
|
|
||||||
case CS_ERR_INTERRUPT:
|
|
||||||
errno = EINTR;
|
|
||||||
break;
|
|
||||||
case CS_ERR_NAME_NOT_FOUND:
|
|
||||||
errno = ENOENT;
|
|
||||||
break;
|
|
||||||
case CS_ERR_NO_RESOURCES:
|
|
||||||
errno = ENOMEM;
|
|
||||||
break;
|
|
||||||
case CS_ERR_NOT_SUPPORTED:
|
|
||||||
errno = EOPNOTSUPP;
|
|
||||||
break;
|
|
||||||
case CS_ERR_BAD_OPERATION:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case CS_ERR_FAILED_OPERATION:
|
|
||||||
errno = EIO;
|
|
||||||
break;
|
|
||||||
case CS_ERR_MESSAGE_ERROR:
|
|
||||||
errno = EIO;
|
|
||||||
break;
|
|
||||||
case CS_ERR_QUEUE_FULL:
|
|
||||||
errno = EXFULL;
|
|
||||||
break;
|
|
||||||
case CS_ERR_QUEUE_NOT_AVAILABLE:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case CS_ERR_BAD_FLAGS:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case CS_ERR_TOO_BIG:
|
|
||||||
errno = E2BIG;
|
|
||||||
break;
|
|
||||||
case CS_ERR_NO_SECTIONS:
|
|
||||||
errno = ENOMEM;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *print_corosync_csid(const char *csid)
|
|
||||||
{
|
|
||||||
static char buf[128];
|
|
||||||
int id;
|
|
||||||
|
|
||||||
memcpy(&id, csid, sizeof(int));
|
|
||||||
sprintf(buf, "%d", id);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void corosync_cpg_deliver_callback (cpg_handle_t handle,
|
|
||||||
const struct cpg_name *groupName,
|
|
||||||
uint32_t nodeid,
|
|
||||||
uint32_t pid,
|
|
||||||
void *msg,
|
|
||||||
size_t msg_len)
|
|
||||||
{
|
|
||||||
int target_nodeid;
|
|
||||||
|
|
||||||
memcpy(&target_nodeid, msg, COROSYNC_CSID_LEN);
|
|
||||||
|
|
||||||
DEBUGLOG("%u got message from nodeid %d for %d. len %zd\n",
|
|
||||||
our_nodeid, nodeid, target_nodeid, msg_len-4);
|
|
||||||
|
|
||||||
if (nodeid != our_nodeid)
|
|
||||||
if (target_nodeid == our_nodeid || target_nodeid == 0)
|
|
||||||
process_message(cluster_client, (char *)msg+COROSYNC_CSID_LEN,
|
|
||||||
msg_len-COROSYNC_CSID_LEN, (char*)&nodeid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void corosync_cpg_confchg_callback(cpg_handle_t handle,
|
|
||||||
const struct cpg_name *groupName,
|
|
||||||
const struct cpg_address *member_list, size_t member_list_entries,
|
|
||||||
const struct cpg_address *left_list, size_t left_list_entries,
|
|
||||||
const struct cpg_address *joined_list, size_t joined_list_entries)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct node_info *ninfo;
|
|
||||||
|
|
||||||
DEBUGLOG("confchg callback. %zd joined, %zd left, %zd members\n",
|
|
||||||
joined_list_entries, left_list_entries, member_list_entries);
|
|
||||||
|
|
||||||
for (i=0; i<joined_list_entries; i++) {
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash,
|
|
||||||
(char *)&joined_list[i].nodeid,
|
|
||||||
COROSYNC_CSID_LEN);
|
|
||||||
if (!ninfo) {
|
|
||||||
ninfo = malloc(sizeof(struct node_info));
|
|
||||||
if (!ninfo) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ninfo->nodeid = joined_list[i].nodeid;
|
|
||||||
dm_hash_insert_binary(node_hash,
|
|
||||||
(char *)&ninfo->nodeid,
|
|
||||||
COROSYNC_CSID_LEN, ninfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ninfo->state = NODE_CLVMD;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0; i<left_list_entries; i++) {
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash,
|
|
||||||
(char *)&left_list[i].nodeid,
|
|
||||||
COROSYNC_CSID_LEN);
|
|
||||||
if (ninfo)
|
|
||||||
ninfo->state = NODE_DOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
num_nodes = member_list_entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _init_cluster(void)
|
|
||||||
{
|
|
||||||
cs_error_t err;
|
|
||||||
|
|
||||||
#ifdef QUORUM_SET /* corosync/quorum.h */
|
|
||||||
uint32_t quorum_type;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
node_hash = dm_hash_create(100);
|
|
||||||
|
|
||||||
err = cpg_initialize(&cpg_handle,
|
|
||||||
&corosync_cpg_callbacks);
|
|
||||||
if (err != CS_OK) {
|
|
||||||
syslog(LOG_ERR, "Cannot initialise Corosync CPG service: %d",
|
|
||||||
err);
|
|
||||||
DEBUGLOG("Cannot initialise Corosync CPG service: %d", err);
|
|
||||||
return cs_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef QUORUM_SET
|
|
||||||
err = quorum_initialize(&quorum_handle,
|
|
||||||
&quorum_callbacks,
|
|
||||||
&quorum_type);
|
|
||||||
|
|
||||||
if (quorum_type != QUORUM_SET) {
|
|
||||||
syslog(LOG_ERR, "Corosync quorum service is not configured");
|
|
||||||
DEBUGLOG("Corosync quorum service is not configured");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
err = quorum_initialize(&quorum_handle,
|
|
||||||
&quorum_callbacks);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (err != CS_OK) {
|
|
||||||
syslog(LOG_ERR, "Cannot initialise Corosync quorum service: %d",
|
|
||||||
err);
|
|
||||||
DEBUGLOG("Cannot initialise Corosync quorum service: %d", err);
|
|
||||||
return cs_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create a lockspace for LV & VG locks to live in */
|
|
||||||
lockspace = dlm_open_lockspace(LOCKSPACE_NAME);
|
|
||||||
if (!lockspace) {
|
|
||||||
lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600);
|
|
||||||
if (!lockspace) {
|
|
||||||
syslog(LOG_ERR, "Unable to create DLM lockspace for CLVM: %m");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
DEBUGLOG("Created DLM lockspace for CLVMD.\n");
|
|
||||||
} else
|
|
||||||
DEBUGLOG("Opened existing DLM lockspace for CLVMD.\n");
|
|
||||||
|
|
||||||
dlm_ls_pthread_init(lockspace);
|
|
||||||
DEBUGLOG("DLM initialisation complete\n");
|
|
||||||
|
|
||||||
/* Connect to the clvmd group */
|
|
||||||
strcpy((char *)cpg_group_name.value, "clvmd");
|
|
||||||
cpg_group_name.length = strlen((char *)cpg_group_name.value);
|
|
||||||
err = cpg_join(cpg_handle, &cpg_group_name);
|
|
||||||
if (err != CS_OK) {
|
|
||||||
cpg_finalize(cpg_handle);
|
|
||||||
quorum_finalize(quorum_handle);
|
|
||||||
dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
|
|
||||||
syslog(LOG_ERR, "Cannot join clvmd process group");
|
|
||||||
DEBUGLOG("Cannot join clvmd process group: %d\n", err);
|
|
||||||
return cs_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cpg_local_get(cpg_handle,
|
|
||||||
&our_nodeid);
|
|
||||||
if (err != CS_OK) {
|
|
||||||
cpg_finalize(cpg_handle);
|
|
||||||
quorum_finalize(quorum_handle);
|
|
||||||
dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
|
|
||||||
syslog(LOG_ERR, "Cannot get local node id\n");
|
|
||||||
return cs_to_errno(err);
|
|
||||||
}
|
|
||||||
DEBUGLOG("Our local node id is %d\n", our_nodeid);
|
|
||||||
|
|
||||||
DEBUGLOG("Connected to Corosync\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _cluster_closedown(void)
|
|
||||||
{
|
|
||||||
dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
|
|
||||||
cpg_finalize(cpg_handle);
|
|
||||||
quorum_finalize(quorum_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _get_our_csid(char *csid)
|
|
||||||
{
|
|
||||||
memcpy(csid, &our_nodeid, sizeof(int));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Corosync doesn't really have nmode names so we
|
|
||||||
just use the node ID in hex instead */
|
|
||||||
static int _csid_from_name(char *csid, const char *name)
|
|
||||||
{
|
|
||||||
int nodeid;
|
|
||||||
struct node_info *ninfo;
|
|
||||||
|
|
||||||
if (sscanf(name, "%x", &nodeid) == 1) {
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash, csid, COROSYNC_CSID_LEN);
|
|
||||||
if (ninfo)
|
|
||||||
return nodeid;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _name_from_csid(const char *csid, char *name)
|
|
||||||
{
|
|
||||||
struct node_info *ninfo;
|
|
||||||
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash, csid, COROSYNC_CSID_LEN);
|
|
||||||
if (!ninfo)
|
|
||||||
{
|
|
||||||
sprintf(name, "UNKNOWN %s", print_corosync_csid(csid));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(name, "%x", ninfo->nodeid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_num_nodes(void)
|
|
||||||
{
|
|
||||||
DEBUGLOG("num_nodes = %d\n", num_nodes);
|
|
||||||
return num_nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Node is now known to be running a clvmd */
|
|
||||||
static void _add_up_node(const char *csid)
|
|
||||||
{
|
|
||||||
struct node_info *ninfo;
|
|
||||||
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash, csid, COROSYNC_CSID_LEN);
|
|
||||||
if (!ninfo) {
|
|
||||||
DEBUGLOG("corosync_add_up_node no node_hash entry for csid %s\n",
|
|
||||||
print_corosync_csid(csid));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGLOG("corosync_add_up_node %d\n", ninfo->nodeid);
|
|
||||||
|
|
||||||
ninfo->state = NODE_CLVMD;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call a callback for each node, so the caller knows whether it's up or down */
|
|
||||||
static int _cluster_do_node_callback(struct local_client *master_client,
|
|
||||||
void (*callback)(struct local_client *,
|
|
||||||
const char *csid, int node_up))
|
|
||||||
{
|
|
||||||
struct dm_hash_node *hn;
|
|
||||||
struct node_info *ninfo;
|
|
||||||
|
|
||||||
dm_hash_iterate(hn, node_hash)
|
|
||||||
{
|
|
||||||
char csid[COROSYNC_CSID_LEN];
|
|
||||||
|
|
||||||
ninfo = dm_hash_get_data(node_hash, hn);
|
|
||||||
memcpy(csid, dm_hash_get_key(node_hash, hn), COROSYNC_CSID_LEN);
|
|
||||||
|
|
||||||
DEBUGLOG("down_callback. node %d, state = %d\n", ninfo->nodeid,
|
|
||||||
ninfo->state);
|
|
||||||
|
|
||||||
if (ninfo->state == NODE_CLVMD)
|
|
||||||
callback(master_client, csid, 1);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Real locking */
|
|
||||||
static int _lock_resource(const char *resource, int mode, int flags, int *lockid)
|
|
||||||
{
|
|
||||||
struct dlm_lksb lksb;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
|
|
||||||
|
|
||||||
if (flags & LKF_CONVERT)
|
|
||||||
lksb.sb_lkid = *lockid;
|
|
||||||
|
|
||||||
err = dlm_ls_lock_wait(lockspace,
|
|
||||||
mode,
|
|
||||||
&lksb,
|
|
||||||
flags,
|
|
||||||
resource,
|
|
||||||
strlen(resource),
|
|
||||||
0,
|
|
||||||
NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (err != 0)
|
|
||||||
{
|
|
||||||
DEBUGLOG("dlm_ls_lock returned %d\n", errno);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
if (lksb.sb_status != 0)
|
|
||||||
{
|
|
||||||
DEBUGLOG("dlm_ls_lock returns lksb.sb_status %d\n", lksb.sb_status);
|
|
||||||
errno = lksb.sb_status;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGLOG("lock_resource returning %d, lock_id=%x\n", err, lksb.sb_lkid);
|
|
||||||
|
|
||||||
*lockid = lksb.sb_lkid;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int _unlock_resource(const char *resource, int lockid)
|
|
||||||
{
|
|
||||||
struct dlm_lksb lksb;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
DEBUGLOG("unlock_resource: %s lockid: %x\n", resource, lockid);
|
|
||||||
lksb.sb_lkid = lockid;
|
|
||||||
|
|
||||||
err = dlm_ls_unlock_wait(lockspace,
|
|
||||||
lockid,
|
|
||||||
0,
|
|
||||||
&lksb);
|
|
||||||
if (err != 0)
|
|
||||||
{
|
|
||||||
DEBUGLOG("Unlock returned %d\n", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
if (lksb.sb_status != EUNLOCK)
|
|
||||||
{
|
|
||||||
DEBUGLOG("dlm_ls_unlock_wait returns lksb.sb_status: %d\n", lksb.sb_status);
|
|
||||||
errno = lksb.sb_status;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _is_quorate(void)
|
|
||||||
{
|
|
||||||
int quorate;
|
|
||||||
if (quorum_getquorate(quorum_handle, &quorate) == CS_OK)
|
|
||||||
return quorate;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_main_cluster_fd(void)
|
|
||||||
{
|
|
||||||
int select_fd;
|
|
||||||
|
|
||||||
cpg_fd_get(cpg_handle, &select_fd);
|
|
||||||
return select_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
|
|
||||||
const char *csid,
|
|
||||||
struct local_client **new_client)
|
|
||||||
{
|
|
||||||
cluster_client = fd;
|
|
||||||
*new_client = NULL;
|
|
||||||
cpg_dispatch(cpg_handle, CS_DISPATCH_ONE);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _cluster_send_message(const void *buf, int msglen, const char *csid,
|
|
||||||
const char *errtext)
|
|
||||||
{
|
|
||||||
static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
struct iovec iov[2];
|
|
||||||
cs_error_t err;
|
|
||||||
int target_node;
|
|
||||||
|
|
||||||
if (csid)
|
|
||||||
memcpy(&target_node, csid, COROSYNC_CSID_LEN);
|
|
||||||
else
|
|
||||||
target_node = 0;
|
|
||||||
|
|
||||||
iov[0].iov_base = &target_node;
|
|
||||||
iov[0].iov_len = sizeof(int);
|
|
||||||
iov[1].iov_base = (char *)buf;
|
|
||||||
iov[1].iov_len = msglen;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&_mutex);
|
|
||||||
err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
|
|
||||||
pthread_mutex_unlock(&_mutex);
|
|
||||||
|
|
||||||
return cs_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_COROSYNC_CONFDB_H
|
|
||||||
/*
|
|
||||||
* We are not necessarily connected to a Red Hat Cluster system,
|
|
||||||
* but if we are, this returns the cluster name from cluster.conf.
|
|
||||||
* I've used confdb rather than ccs to reduce the inter-package
|
|
||||||
* dependancies as well as to allow people to set a cluster name
|
|
||||||
* for themselves even if they are not running on RH cluster.
|
|
||||||
*/
|
|
||||||
static int _get_cluster_name(char *buf, int buflen)
|
|
||||||
{
|
|
||||||
confdb_handle_t handle;
|
|
||||||
int result;
|
|
||||||
size_t namelen = buflen;
|
|
||||||
hdb_handle_t cluster_handle;
|
|
||||||
confdb_callbacks_t callbacks = {
|
|
||||||
.confdb_key_change_notify_fn = NULL,
|
|
||||||
.confdb_object_create_change_notify_fn = NULL,
|
|
||||||
.confdb_object_delete_change_notify_fn = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This is a default in case everything else fails */
|
|
||||||
strncpy(buf, "Corosync", buflen);
|
|
||||||
|
|
||||||
/* Look for a cluster name in confdb */
|
|
||||||
result = confdb_initialize (&handle, &callbacks);
|
|
||||||
if (result != CS_OK)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
result = confdb_object_find_start(handle, OBJECT_PARENT_HANDLE);
|
|
||||||
if (result != CS_OK)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
result = confdb_object_find(handle, OBJECT_PARENT_HANDLE, (void *)"cluster", strlen("cluster"), &cluster_handle);
|
|
||||||
if (result != CS_OK)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
result = confdb_key_get(handle, cluster_handle, (void *)"name", strlen("name"), buf, &namelen);
|
|
||||||
if (result != CS_OK)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
buf[namelen] = '\0';
|
|
||||||
|
|
||||||
out:
|
|
||||||
confdb_finalize(handle);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined HAVE_COROSYNC_CMAP_H
|
|
||||||
|
|
||||||
static int _get_cluster_name(char *buf, int buflen)
|
|
||||||
{
|
|
||||||
cmap_handle_t cmap_handle = 0;
|
|
||||||
int result;
|
|
||||||
char *name = NULL;
|
|
||||||
|
|
||||||
/* This is a default in case everything else fails */
|
|
||||||
strncpy(buf, "Corosync", buflen);
|
|
||||||
|
|
||||||
/* Look for a cluster name in cmap */
|
|
||||||
result = cmap_initialize(&cmap_handle);
|
|
||||||
if (result != CS_OK)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
result = cmap_get_string(cmap_handle, "totem.cluster_name", &name);
|
|
||||||
if (result != CS_OK)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
memset(buf, 0, buflen);
|
|
||||||
strncpy(buf, name, buflen - 1);
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (name)
|
|
||||||
free(name);
|
|
||||||
cmap_finalize(cmap_handle);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct cluster_ops _cluster_corosync_ops = {
|
|
||||||
.name = "corosync",
|
|
||||||
.cluster_init_completed = NULL,
|
|
||||||
.cluster_send_message = _cluster_send_message,
|
|
||||||
.name_from_csid = _name_from_csid,
|
|
||||||
.csid_from_name = _csid_from_name,
|
|
||||||
.get_num_nodes = _get_num_nodes,
|
|
||||||
.cluster_fd_callback = _cluster_fd_callback,
|
|
||||||
.get_main_cluster_fd = _get_main_cluster_fd,
|
|
||||||
.cluster_do_node_callback = _cluster_do_node_callback,
|
|
||||||
.is_quorate = _is_quorate,
|
|
||||||
.get_our_csid = _get_our_csid,
|
|
||||||
.add_up_node = _add_up_node,
|
|
||||||
.reread_config = NULL,
|
|
||||||
.cluster_closedown = _cluster_closedown,
|
|
||||||
.get_cluster_name = _get_cluster_name,
|
|
||||||
.sync_lock = _lock_resource,
|
|
||||||
.sync_unlock = _unlock_resource,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cluster_ops *init_corosync_cluster(void)
|
|
||||||
{
|
|
||||||
if (!_init_cluster())
|
|
||||||
return &_cluster_corosync_ops;
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
@@ -1,687 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This provides the interface between clvmd and OpenAIS as the cluster
|
|
||||||
* and lock manager.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "clvmd-common.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
#include <openais/saAis.h>
|
|
||||||
#include <openais/saLck.h>
|
|
||||||
|
|
||||||
#include <corosync/corotypes.h>
|
|
||||||
#include <corosync/cpg.h>
|
|
||||||
|
|
||||||
#include "locking.h"
|
|
||||||
#include "clvm.h"
|
|
||||||
#include "clvmd-comms.h"
|
|
||||||
#include "lvm-functions.h"
|
|
||||||
#include "clvmd.h"
|
|
||||||
|
|
||||||
/* Timeout value for several openais calls */
|
|
||||||
#define TIMEOUT 10
|
|
||||||
|
|
||||||
static void openais_cpg_deliver_callback (cpg_handle_t handle,
|
|
||||||
const struct cpg_name *groupName,
|
|
||||||
uint32_t nodeid,
|
|
||||||
uint32_t pid,
|
|
||||||
void *msg,
|
|
||||||
size_t msg_len);
|
|
||||||
static void openais_cpg_confchg_callback(cpg_handle_t handle,
|
|
||||||
const struct cpg_name *groupName,
|
|
||||||
const struct cpg_address *member_list, size_t member_list_entries,
|
|
||||||
const struct cpg_address *left_list, size_t left_list_entries,
|
|
||||||
const struct cpg_address *joined_list, size_t joined_list_entries);
|
|
||||||
|
|
||||||
static void _cluster_closedown(void);
|
|
||||||
|
|
||||||
/* Hash list of nodes in the cluster */
|
|
||||||
static struct dm_hash_table *node_hash;
|
|
||||||
|
|
||||||
/* For associating lock IDs & resource handles */
|
|
||||||
static struct dm_hash_table *lock_hash;
|
|
||||||
|
|
||||||
/* Number of active nodes */
|
|
||||||
static int num_nodes;
|
|
||||||
static unsigned int our_nodeid;
|
|
||||||
|
|
||||||
static struct local_client *cluster_client;
|
|
||||||
|
|
||||||
/* OpenAIS handles */
|
|
||||||
static cpg_handle_t cpg_handle;
|
|
||||||
static SaLckHandleT lck_handle;
|
|
||||||
|
|
||||||
static struct cpg_name cpg_group_name;
|
|
||||||
|
|
||||||
/* Openais callback structs */
|
|
||||||
cpg_callbacks_t openais_cpg_callbacks = {
|
|
||||||
.cpg_deliver_fn = openais_cpg_deliver_callback,
|
|
||||||
.cpg_confchg_fn = openais_cpg_confchg_callback,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct node_info
|
|
||||||
{
|
|
||||||
enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state;
|
|
||||||
int nodeid;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct lock_info
|
|
||||||
{
|
|
||||||
SaLckResourceHandleT res_handle;
|
|
||||||
SaLckLockIdT lock_id;
|
|
||||||
SaNameT lock_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Set errno to something approximating the right value and return 0 or -1 */
|
|
||||||
static int ais_to_errno(SaAisErrorT err)
|
|
||||||
{
|
|
||||||
switch(err)
|
|
||||||
{
|
|
||||||
case SA_AIS_OK:
|
|
||||||
return 0;
|
|
||||||
case SA_AIS_ERR_LIBRARY:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_VERSION:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_INIT:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_TIMEOUT:
|
|
||||||
errno = ETIME;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_TRY_AGAIN:
|
|
||||||
errno = EAGAIN;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_INVALID_PARAM:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_NO_MEMORY:
|
|
||||||
errno = ENOMEM;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_BAD_HANDLE:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_BUSY:
|
|
||||||
errno = EBUSY;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_ACCESS:
|
|
||||||
errno = EPERM;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_NOT_EXIST:
|
|
||||||
errno = ENOENT;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_NAME_TOO_LONG:
|
|
||||||
errno = ENAMETOOLONG;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_EXIST:
|
|
||||||
errno = EEXIST;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_NO_SPACE:
|
|
||||||
errno = ENOSPC;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_INTERRUPT:
|
|
||||||
errno = EINTR;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_NAME_NOT_FOUND:
|
|
||||||
errno = ENOENT;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_NO_RESOURCES:
|
|
||||||
errno = ENOMEM;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_NOT_SUPPORTED:
|
|
||||||
errno = EOPNOTSUPP;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_BAD_OPERATION:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_FAILED_OPERATION:
|
|
||||||
errno = EIO;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_MESSAGE_ERROR:
|
|
||||||
errno = EIO;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_QUEUE_FULL:
|
|
||||||
errno = EXFULL;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_QUEUE_NOT_AVAILABLE:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_BAD_FLAGS:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_TOO_BIG:
|
|
||||||
errno = E2BIG;
|
|
||||||
break;
|
|
||||||
case SA_AIS_ERR_NO_SECTIONS:
|
|
||||||
errno = ENOMEM;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *print_openais_csid(const char *csid)
|
|
||||||
{
|
|
||||||
static char buf[128];
|
|
||||||
int id;
|
|
||||||
|
|
||||||
memcpy(&id, csid, sizeof(int));
|
|
||||||
sprintf(buf, "%d", id);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int add_internal_client(int fd, fd_callback_t callback)
|
|
||||||
{
|
|
||||||
struct local_client *client;
|
|
||||||
|
|
||||||
DEBUGLOG("Add_internal_client, fd = %d\n", fd);
|
|
||||||
|
|
||||||
if (!(client = dm_zalloc(sizeof(*client)))) {
|
|
||||||
DEBUGLOG("malloc failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->fd = fd;
|
|
||||||
client->type = CLUSTER_INTERNAL;
|
|
||||||
client->callback = callback;
|
|
||||||
add_client(client);
|
|
||||||
|
|
||||||
/* Set Close-on-exec */
|
|
||||||
fcntl(fd, F_SETFD, 1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void openais_cpg_deliver_callback (cpg_handle_t handle,
|
|
||||||
const struct cpg_name *groupName,
|
|
||||||
uint32_t nodeid,
|
|
||||||
uint32_t pid,
|
|
||||||
void *msg,
|
|
||||||
size_t msg_len)
|
|
||||||
{
|
|
||||||
int target_nodeid;
|
|
||||||
|
|
||||||
memcpy(&target_nodeid, msg, OPENAIS_CSID_LEN);
|
|
||||||
|
|
||||||
DEBUGLOG("%u got message from nodeid %d for %d. len %" PRIsize_t "\n",
|
|
||||||
our_nodeid, nodeid, target_nodeid, msg_len-4);
|
|
||||||
|
|
||||||
if (nodeid != our_nodeid)
|
|
||||||
if (target_nodeid == our_nodeid || target_nodeid == 0)
|
|
||||||
process_message(cluster_client, (char *)msg+OPENAIS_CSID_LEN,
|
|
||||||
msg_len-OPENAIS_CSID_LEN, (char*)&nodeid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void openais_cpg_confchg_callback(cpg_handle_t handle,
|
|
||||||
const struct cpg_name *groupName,
|
|
||||||
const struct cpg_address *member_list, size_t member_list_entries,
|
|
||||||
const struct cpg_address *left_list, size_t left_list_entries,
|
|
||||||
const struct cpg_address *joined_list, size_t joined_list_entries)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct node_info *ninfo;
|
|
||||||
|
|
||||||
DEBUGLOG("confchg callback. %" PRIsize_t " joined, "
|
|
||||||
FMTsize_t " left, %" PRIsize_t " members\n",
|
|
||||||
joined_list_entries, left_list_entries, member_list_entries);
|
|
||||||
|
|
||||||
for (i=0; i<joined_list_entries; i++) {
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash,
|
|
||||||
(char *)&joined_list[i].nodeid,
|
|
||||||
OPENAIS_CSID_LEN);
|
|
||||||
if (!ninfo) {
|
|
||||||
ninfo = malloc(sizeof(struct node_info));
|
|
||||||
if (!ninfo) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ninfo->nodeid = joined_list[i].nodeid;
|
|
||||||
dm_hash_insert_binary(node_hash,
|
|
||||||
(char *)&ninfo->nodeid,
|
|
||||||
OPENAIS_CSID_LEN, ninfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ninfo->state = NODE_CLVMD;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0; i<left_list_entries; i++) {
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash,
|
|
||||||
(char *)&left_list[i].nodeid,
|
|
||||||
OPENAIS_CSID_LEN);
|
|
||||||
if (ninfo)
|
|
||||||
ninfo->state = NODE_DOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0; i<member_list_entries; i++) {
|
|
||||||
if (member_list[i].nodeid == 0) continue;
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash,
|
|
||||||
(char *)&member_list[i].nodeid,
|
|
||||||
OPENAIS_CSID_LEN);
|
|
||||||
if (!ninfo) {
|
|
||||||
ninfo = malloc(sizeof(struct node_info));
|
|
||||||
if (!ninfo) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ninfo->nodeid = member_list[i].nodeid;
|
|
||||||
dm_hash_insert_binary(node_hash,
|
|
||||||
(char *)&ninfo->nodeid,
|
|
||||||
OPENAIS_CSID_LEN, ninfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ninfo->state = NODE_CLVMD;
|
|
||||||
}
|
|
||||||
|
|
||||||
num_nodes = member_list_entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lck_dispatch(struct local_client *client, char *buf, int len,
|
|
||||||
const char *csid, struct local_client **new_client)
|
|
||||||
{
|
|
||||||
*new_client = NULL;
|
|
||||||
saLckDispatch(lck_handle, SA_DISPATCH_ONE);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _init_cluster(void)
|
|
||||||
{
|
|
||||||
SaAisErrorT err;
|
|
||||||
SaVersionT ver = { 'B', 1, 1 };
|
|
||||||
int select_fd;
|
|
||||||
|
|
||||||
node_hash = dm_hash_create(100);
|
|
||||||
lock_hash = dm_hash_create(10);
|
|
||||||
|
|
||||||
err = cpg_initialize(&cpg_handle,
|
|
||||||
&openais_cpg_callbacks);
|
|
||||||
if (err != SA_AIS_OK) {
|
|
||||||
syslog(LOG_ERR, "Cannot initialise OpenAIS CPG service: %d",
|
|
||||||
err);
|
|
||||||
DEBUGLOG("Cannot initialise OpenAIS CPG service: %d", err);
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = saLckInitialize(&lck_handle,
|
|
||||||
NULL,
|
|
||||||
&ver);
|
|
||||||
if (err != SA_AIS_OK) {
|
|
||||||
cpg_initialize(&cpg_handle, &openais_cpg_callbacks);
|
|
||||||
syslog(LOG_ERR, "Cannot initialise OpenAIS lock service: %d",
|
|
||||||
err);
|
|
||||||
DEBUGLOG("Cannot initialise OpenAIS lock service: %d\n\n", err);
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Connect to the clvmd group */
|
|
||||||
strcpy((char *)cpg_group_name.value, "clvmd");
|
|
||||||
cpg_group_name.length = strlen((char *)cpg_group_name.value);
|
|
||||||
err = cpg_join(cpg_handle, &cpg_group_name);
|
|
||||||
if (err != SA_AIS_OK) {
|
|
||||||
cpg_finalize(cpg_handle);
|
|
||||||
saLckFinalize(lck_handle);
|
|
||||||
syslog(LOG_ERR, "Cannot join clvmd process group");
|
|
||||||
DEBUGLOG("Cannot join clvmd process group: %d\n", err);
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cpg_local_get(cpg_handle,
|
|
||||||
&our_nodeid);
|
|
||||||
if (err != SA_AIS_OK) {
|
|
||||||
cpg_finalize(cpg_handle);
|
|
||||||
saLckFinalize(lck_handle);
|
|
||||||
syslog(LOG_ERR, "Cannot get local node id\n");
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
DEBUGLOG("Our local node id is %d\n", our_nodeid);
|
|
||||||
|
|
||||||
saLckSelectionObjectGet(lck_handle, (SaSelectionObjectT *)&select_fd);
|
|
||||||
add_internal_client(select_fd, lck_dispatch);
|
|
||||||
|
|
||||||
DEBUGLOG("Connected to OpenAIS\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _cluster_closedown(void)
|
|
||||||
{
|
|
||||||
saLckFinalize(lck_handle);
|
|
||||||
cpg_finalize(cpg_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _get_our_csid(char *csid)
|
|
||||||
{
|
|
||||||
memcpy(csid, &our_nodeid, sizeof(int));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OpenAIS doesn't really have nmode names so we
|
|
||||||
just use the node ID in hex instead */
|
|
||||||
static int _csid_from_name(char *csid, const char *name)
|
|
||||||
{
|
|
||||||
int nodeid;
|
|
||||||
struct node_info *ninfo;
|
|
||||||
|
|
||||||
if (sscanf(name, "%x", &nodeid) == 1) {
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
|
|
||||||
if (ninfo)
|
|
||||||
return nodeid;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _name_from_csid(const char *csid, char *name)
|
|
||||||
{
|
|
||||||
struct node_info *ninfo;
|
|
||||||
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
|
|
||||||
if (!ninfo)
|
|
||||||
{
|
|
||||||
sprintf(name, "UNKNOWN %s", print_openais_csid(csid));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(name, "%x", ninfo->nodeid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_num_nodes()
|
|
||||||
{
|
|
||||||
DEBUGLOG("num_nodes = %d\n", num_nodes);
|
|
||||||
return num_nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Node is now known to be running a clvmd */
|
|
||||||
static void _add_up_node(const char *csid)
|
|
||||||
{
|
|
||||||
struct node_info *ninfo;
|
|
||||||
|
|
||||||
ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
|
|
||||||
if (!ninfo) {
|
|
||||||
DEBUGLOG("openais_add_up_node no node_hash entry for csid %s\n",
|
|
||||||
print_openais_csid(csid));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGLOG("openais_add_up_node %d\n", ninfo->nodeid);
|
|
||||||
|
|
||||||
ninfo->state = NODE_CLVMD;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call a callback for each node, so the caller knows whether it's up or down */
|
|
||||||
static int _cluster_do_node_callback(struct local_client *master_client,
|
|
||||||
void (*callback)(struct local_client *,
|
|
||||||
const char *csid, int node_up))
|
|
||||||
{
|
|
||||||
struct dm_hash_node *hn;
|
|
||||||
struct node_info *ninfo;
|
|
||||||
int somedown = 0;
|
|
||||||
|
|
||||||
dm_hash_iterate(hn, node_hash)
|
|
||||||
{
|
|
||||||
char csid[OPENAIS_CSID_LEN];
|
|
||||||
|
|
||||||
ninfo = dm_hash_get_data(node_hash, hn);
|
|
||||||
memcpy(csid, dm_hash_get_key(node_hash, hn), OPENAIS_CSID_LEN);
|
|
||||||
|
|
||||||
DEBUGLOG("down_callback. node %d, state = %d\n", ninfo->nodeid,
|
|
||||||
ninfo->state);
|
|
||||||
|
|
||||||
if (ninfo->state != NODE_DOWN)
|
|
||||||
callback(master_client, csid, ninfo->state == NODE_CLVMD);
|
|
||||||
if (ninfo->state != NODE_CLVMD)
|
|
||||||
somedown = -1;
|
|
||||||
}
|
|
||||||
return somedown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Real locking */
|
|
||||||
static int _lock_resource(char *resource, int mode, int flags, int *lockid)
|
|
||||||
{
|
|
||||||
struct lock_info *linfo;
|
|
||||||
SaLckResourceHandleT res_handle;
|
|
||||||
SaAisErrorT err;
|
|
||||||
SaLckLockIdT lock_id;
|
|
||||||
SaLckLockStatusT lockStatus;
|
|
||||||
|
|
||||||
/* This needs to be converted from DLM/LVM2 value for OpenAIS LCK */
|
|
||||||
if (flags & LCK_NONBLOCK) flags = SA_LCK_LOCK_NO_QUEUE;
|
|
||||||
|
|
||||||
linfo = malloc(sizeof(struct lock_info));
|
|
||||||
if (!linfo)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
|
|
||||||
|
|
||||||
linfo->lock_name.length = strlen(resource)+1;
|
|
||||||
strcpy((char *)linfo->lock_name.value, resource);
|
|
||||||
|
|
||||||
err = saLckResourceOpen(lck_handle, &linfo->lock_name,
|
|
||||||
SA_LCK_RESOURCE_CREATE, TIMEOUT, &res_handle);
|
|
||||||
if (err != SA_AIS_OK)
|
|
||||||
{
|
|
||||||
DEBUGLOG("ResourceOpen returned %d\n", err);
|
|
||||||
free(linfo);
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = saLckResourceLock(
|
|
||||||
res_handle,
|
|
||||||
&lock_id,
|
|
||||||
mode,
|
|
||||||
flags,
|
|
||||||
0,
|
|
||||||
SA_TIME_END,
|
|
||||||
&lockStatus);
|
|
||||||
if (err != SA_AIS_OK && lockStatus != SA_LCK_LOCK_GRANTED)
|
|
||||||
{
|
|
||||||
free(linfo);
|
|
||||||
saLckResourceClose(res_handle);
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait for it to complete */
|
|
||||||
|
|
||||||
DEBUGLOG("lock_resource returning %d, lock_id=%" PRIx64 "\n",
|
|
||||||
err, lock_id);
|
|
||||||
|
|
||||||
linfo->lock_id = lock_id;
|
|
||||||
linfo->res_handle = res_handle;
|
|
||||||
|
|
||||||
dm_hash_insert(lock_hash, resource, linfo);
|
|
||||||
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int _unlock_resource(char *resource, int lockid)
|
|
||||||
{
|
|
||||||
SaAisErrorT err;
|
|
||||||
struct lock_info *linfo;
|
|
||||||
|
|
||||||
DEBUGLOG("unlock_resource %s\n", resource);
|
|
||||||
linfo = dm_hash_lookup(lock_hash, resource);
|
|
||||||
if (!linfo)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
DEBUGLOG("unlock_resource: lockid: %" PRIx64 "\n", linfo->lock_id);
|
|
||||||
err = saLckResourceUnlock(linfo->lock_id, SA_TIME_END);
|
|
||||||
if (err != SA_AIS_OK)
|
|
||||||
{
|
|
||||||
DEBUGLOG("Unlock returned %d\n", err);
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release the resource */
|
|
||||||
dm_hash_remove(lock_hash, resource);
|
|
||||||
saLckResourceClose(linfo->res_handle);
|
|
||||||
free(linfo);
|
|
||||||
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
char lock1[strlen(resource)+3];
|
|
||||||
char lock2[strlen(resource)+3];
|
|
||||||
|
|
||||||
snprintf(lock1, sizeof(lock1), "%s-1", resource);
|
|
||||||
snprintf(lock2, sizeof(lock2), "%s-2", resource);
|
|
||||||
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case LCK_EXCL:
|
|
||||||
status = _lock_resource(lock1, SA_LCK_EX_LOCK_MODE, flags, lockid);
|
|
||||||
if (status)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* If we can't get this lock too then bail out */
|
|
||||||
status = _lock_resource(lock2, SA_LCK_EX_LOCK_MODE, LCK_NONBLOCK,
|
|
||||||
lockid);
|
|
||||||
if (status == SA_LCK_LOCK_NOT_QUEUED)
|
|
||||||
{
|
|
||||||
_unlock_resource(lock1, *lockid);
|
|
||||||
status = -1;
|
|
||||||
errno = EAGAIN;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LCK_PREAD:
|
|
||||||
case LCK_READ:
|
|
||||||
status = _lock_resource(lock1, SA_LCK_PR_LOCK_MODE, flags, lockid);
|
|
||||||
if (status)
|
|
||||||
goto out;
|
|
||||||
_unlock_resource(lock2, *lockid);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LCK_WRITE:
|
|
||||||
status = _lock_resource(lock2, SA_LCK_EX_LOCK_MODE, flags, lockid);
|
|
||||||
if (status)
|
|
||||||
goto out;
|
|
||||||
_unlock_resource(lock1, *lockid);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
status = -1;
|
|
||||||
errno = EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
*lockid = mode;
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _sync_unlock(const char *resource, int lockid)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
char lock1[strlen(resource)+3];
|
|
||||||
char lock2[strlen(resource)+3];
|
|
||||||
|
|
||||||
snprintf(lock1, sizeof(lock1), "%s-1", resource);
|
|
||||||
snprintf(lock2, sizeof(lock2), "%s-2", resource);
|
|
||||||
|
|
||||||
_unlock_resource(lock1, lockid);
|
|
||||||
_unlock_resource(lock2, lockid);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We are always quorate ! */
|
|
||||||
static int _is_quorate()
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_main_cluster_fd(void)
|
|
||||||
{
|
|
||||||
int select_fd;
|
|
||||||
|
|
||||||
cpg_fd_get(cpg_handle, &select_fd);
|
|
||||||
return select_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
|
|
||||||
const char *csid,
|
|
||||||
struct local_client **new_client)
|
|
||||||
{
|
|
||||||
cluster_client = fd;
|
|
||||||
*new_client = NULL;
|
|
||||||
cpg_dispatch(cpg_handle, SA_DISPATCH_ONE);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _cluster_send_message(const void *buf, int msglen, const char *csid,
|
|
||||||
const char *errtext)
|
|
||||||
{
|
|
||||||
struct iovec iov[2];
|
|
||||||
SaAisErrorT err;
|
|
||||||
int target_node;
|
|
||||||
|
|
||||||
if (csid)
|
|
||||||
memcpy(&target_node, csid, OPENAIS_CSID_LEN);
|
|
||||||
else
|
|
||||||
target_node = 0;
|
|
||||||
|
|
||||||
iov[0].iov_base = &target_node;
|
|
||||||
iov[0].iov_len = sizeof(int);
|
|
||||||
iov[1].iov_base = (char *)buf;
|
|
||||||
iov[1].iov_len = msglen;
|
|
||||||
|
|
||||||
err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
|
|
||||||
return ais_to_errno(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We don't have a cluster name to report here */
|
|
||||||
static int _get_cluster_name(char *buf, int buflen)
|
|
||||||
{
|
|
||||||
strncpy(buf, "OpenAIS", buflen);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct cluster_ops _cluster_openais_ops = {
|
|
||||||
.name = "openais",
|
|
||||||
.cluster_init_completed = NULL,
|
|
||||||
.cluster_send_message = _cluster_send_message,
|
|
||||||
.name_from_csid = _name_from_csid,
|
|
||||||
.csid_from_name = _csid_from_name,
|
|
||||||
.get_num_nodes = _get_num_nodes,
|
|
||||||
.cluster_fd_callback = _cluster_fd_callback,
|
|
||||||
.get_main_cluster_fd = _get_main_cluster_fd,
|
|
||||||
.cluster_do_node_callback = _cluster_do_node_callback,
|
|
||||||
.is_quorate = _is_quorate,
|
|
||||||
.get_our_csid = _get_our_csid,
|
|
||||||
.add_up_node = _add_up_node,
|
|
||||||
.reread_config = NULL,
|
|
||||||
.cluster_closedown = _cluster_closedown,
|
|
||||||
.get_cluster_name = _get_cluster_name,
|
|
||||||
.sync_lock = _sync_lock,
|
|
||||||
.sync_unlock = _sync_unlock,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cluster_ops *init_openais_cluster(void)
|
|
||||||
{
|
|
||||||
if (!_init_cluster())
|
|
||||||
return &_cluster_openais_ops;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
@@ -1,382 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009-2013 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "clvmd-common.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include "locking.h"
|
|
||||||
#include "clvm.h"
|
|
||||||
#include "clvmd-comms.h"
|
|
||||||
#include "clvmd.h"
|
|
||||||
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
static const char SINGLENODE_CLVMD_SOCKNAME[] = DEFAULT_RUN_DIR "/clvmd_singlenode.sock";
|
|
||||||
static int listen_fd = -1;
|
|
||||||
|
|
||||||
static struct dm_hash_table *_locks;
|
|
||||||
static int _lockid;
|
|
||||||
|
|
||||||
static pthread_mutex_t _lock_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
/* Using one common condition for all locks for simplicity */
|
|
||||||
static pthread_cond_t _lock_cond = PTHREAD_COND_INITIALIZER;
|
|
||||||
|
|
||||||
struct lock {
|
|
||||||
struct dm_list list;
|
|
||||||
int lockid;
|
|
||||||
int mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void close_comms(void)
|
|
||||||
{
|
|
||||||
if (listen_fd != -1 && close(listen_fd))
|
|
||||||
stack;
|
|
||||||
(void)unlink(SINGLENODE_CLVMD_SOCKNAME);
|
|
||||||
listen_fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int init_comms(void)
|
|
||||||
{
|
|
||||||
mode_t old_mask;
|
|
||||||
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
|
||||||
|
|
||||||
if (!dm_strncpy(addr.sun_path, SINGLENODE_CLVMD_SOCKNAME,
|
|
||||||
sizeof(addr.sun_path))) {
|
|
||||||
DEBUGLOG("%s: singlenode socket name too long.",
|
|
||||||
SINGLENODE_CLVMD_SOCKNAME);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
close_comms();
|
|
||||||
|
|
||||||
(void) dm_prepare_selinux_context(SINGLENODE_CLVMD_SOCKNAME, S_IFSOCK);
|
|
||||||
old_mask = umask(0077);
|
|
||||||
|
|
||||||
listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
||||||
if (listen_fd < 0) {
|
|
||||||
DEBUGLOG("Can't create local socket: %s\n", strerror(errno));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
/* Set Close-on-exec */
|
|
||||||
if (fcntl(listen_fd, F_SETFD, 1)) {
|
|
||||||
DEBUGLOG("Setting CLOEXEC on client fd failed: %s\n", strerror(errno));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
||||||
DEBUGLOG("Can't bind local socket: %s\n", strerror(errno));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (listen(listen_fd, 10) < 0) {
|
|
||||||
DEBUGLOG("Can't listen local socket: %s\n", strerror(errno));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
umask(old_mask);
|
|
||||||
(void) dm_prepare_selinux_context(NULL, 0);
|
|
||||||
return 0;
|
|
||||||
error:
|
|
||||||
umask(old_mask);
|
|
||||||
(void) dm_prepare_selinux_context(NULL, 0);
|
|
||||||
close_comms();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _init_cluster(void)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (!(_locks = dm_hash_create(128))) {
|
|
||||||
DEBUGLOG("Failed to allocate single-node hash table.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = init_comms();
|
|
||||||
if (r) {
|
|
||||||
dm_hash_destroy(_locks);
|
|
||||||
_locks = NULL;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGLOG("Single-node cluster initialised.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _cluster_closedown(void)
|
|
||||||
{
|
|
||||||
close_comms();
|
|
||||||
|
|
||||||
/* If there is any awaited resource, kill it softly */
|
|
||||||
pthread_mutex_lock(&_lock_mutex);
|
|
||||||
dm_hash_destroy(_locks);
|
|
||||||
_locks = NULL;
|
|
||||||
_lockid = 0;
|
|
||||||
pthread_cond_broadcast(&_lock_cond); /* wakeup waiters */
|
|
||||||
pthread_mutex_unlock(&_lock_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _get_our_csid(char *csid)
|
|
||||||
{
|
|
||||||
int nodeid = 1;
|
|
||||||
memcpy(csid, &nodeid, sizeof(int));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _csid_from_name(char *csid, const char *name)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _name_from_csid(const char *csid, char *name)
|
|
||||||
{
|
|
||||||
strcpy(name, "SINGLENODE");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_num_nodes(void)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Node is now known to be running a clvmd */
|
|
||||||
static void _add_up_node(const char *csid)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call a callback for each node, so the caller knows whether it's up or down */
|
|
||||||
static int _cluster_do_node_callback(struct local_client *master_client,
|
|
||||||
void (*callback)(struct local_client *,
|
|
||||||
const char *csid, int node_up))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _lock_file(const char *file, uint32_t flags);
|
|
||||||
|
|
||||||
static const char *_get_mode(int mode)
|
|
||||||
{
|
|
||||||
switch (mode) {
|
|
||||||
case LCK_NULL: return "NULL";
|
|
||||||
case LCK_READ: return "READ";
|
|
||||||
case LCK_PREAD: return "PREAD";
|
|
||||||
case LCK_WRITE: return "WRITE";
|
|
||||||
case LCK_EXCL: return "EXCLUSIVE";
|
|
||||||
case LCK_UNLOCK: return "UNLOCK";
|
|
||||||
default: return "????";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Real locking */
|
|
||||||
static int _lock_resource(const char *resource, int mode, int flags, int *lockid)
|
|
||||||
{
|
|
||||||
/* DLM table of allowed transition states */
|
|
||||||
static const int _dlm_table[6][6] = {
|
|
||||||
/* Mode NL CR CW PR PW EX */
|
|
||||||
/* NL */ { 1, 1, 1, 1, 1, 1},
|
|
||||||
/* CR */ { 1, 1, 1, 1, 1, 0},
|
|
||||||
/* CW */ { 1, 1, 1, 0, 0, 0},
|
|
||||||
/* PR */ { 1, 1, 0, 1, 0, 0},
|
|
||||||
/* PW */ { 1, 1, 0, 0, 0, 0},
|
|
||||||
/* EX */ { 1, 0, 0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct lock *lck = NULL, *lckt;
|
|
||||||
struct dm_list *head;
|
|
||||||
|
|
||||||
DEBUGLOG("Locking resource %s, flags=0x%02x (%s%s%s), mode=%s (%d)\n",
|
|
||||||
resource, flags,
|
|
||||||
(flags & LCKF_NOQUEUE) ? "NOQUEUE" : "",
|
|
||||||
((flags & (LCKF_NOQUEUE | LCKF_CONVERT)) ==
|
|
||||||
(LCKF_NOQUEUE | LCKF_CONVERT)) ? "|" : "",
|
|
||||||
(flags & LCKF_CONVERT) ? "CONVERT" : "",
|
|
||||||
_get_mode(mode), mode);
|
|
||||||
|
|
||||||
mode &= LCK_TYPE_MASK;
|
|
||||||
pthread_mutex_lock(&_lock_mutex);
|
|
||||||
|
|
||||||
retry:
|
|
||||||
if (!(head = dm_hash_lookup(_locks, resource))) {
|
|
||||||
if (flags & LCKF_CONVERT) {
|
|
||||||
/* In real DLM, lock is identified only by lockid, resource is not used */
|
|
||||||
DEBUGLOG("Unlocked resource %s cannot be converted\n", resource);
|
|
||||||
goto_bad;
|
|
||||||
}
|
|
||||||
/* Add new locked resource */
|
|
||||||
if (!(head = dm_malloc(sizeof(struct dm_list))) ||
|
|
||||||
!dm_hash_insert(_locks, resource, head)) {
|
|
||||||
dm_free(head);
|
|
||||||
goto_bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_list_init(head);
|
|
||||||
} else /* Update/convert locked resource */
|
|
||||||
dm_list_iterate_items(lck, head) {
|
|
||||||
/* Check is all locks are compatible with requested lock */
|
|
||||||
if (flags & LCKF_CONVERT) {
|
|
||||||
if (lck->lockid != *lockid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
DEBUGLOG("Converting resource %s lockid=%d mode:%s -> %s...\n",
|
|
||||||
resource, lck->lockid, _get_mode(lck->mode), _get_mode(mode));
|
|
||||||
dm_list_iterate_items(lckt, head) {
|
|
||||||
if ((lckt->lockid != *lockid) &&
|
|
||||||
!_dlm_table[mode][lckt->mode]) {
|
|
||||||
if (!(flags & LCKF_NOQUEUE) &&
|
|
||||||
/* TODO: Real dlm uses here conversion queues */
|
|
||||||
!pthread_cond_wait(&_lock_cond, &_lock_mutex) &&
|
|
||||||
_locks) /* End of the game? */
|
|
||||||
goto retry;
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lck->mode = mode; /* Lock is now converted */
|
|
||||||
goto out;
|
|
||||||
} else if (!_dlm_table[mode][lck->mode]) {
|
|
||||||
DEBUGLOG("Resource %s already locked lockid=%d, mode:%s\n",
|
|
||||||
resource, lck->lockid, _get_mode(lck->mode));
|
|
||||||
if (!(flags & LCKF_NOQUEUE) &&
|
|
||||||
!pthread_cond_wait(&_lock_cond, &_lock_mutex) &&
|
|
||||||
_locks) { /* End of the game? */
|
|
||||||
DEBUGLOG("Resource %s retrying lock in mode:%s...\n",
|
|
||||||
resource, _get_mode(mode));
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(flags & LCKF_CONVERT)) {
|
|
||||||
if (!(lck = dm_malloc(sizeof(struct lock))))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
*lockid = lck->lockid = ++_lockid;
|
|
||||||
lck->mode = mode;
|
|
||||||
dm_list_add(head, &lck->list);
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
pthread_cond_broadcast(&_lock_cond); /* to wakeup waiters */
|
|
||||||
pthread_mutex_unlock(&_lock_mutex);
|
|
||||||
DEBUGLOG("Locked resource %s, lockid=%d, mode=%s\n",
|
|
||||||
resource, lck->lockid, _get_mode(lck->mode));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
bad:
|
|
||||||
pthread_cond_broadcast(&_lock_cond); /* to wakeup waiters */
|
|
||||||
pthread_mutex_unlock(&_lock_mutex);
|
|
||||||
DEBUGLOG("Failed to lock resource %s\n", resource);
|
|
||||||
|
|
||||||
return 1; /* fail */
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _unlock_resource(const char *resource, int lockid)
|
|
||||||
{
|
|
||||||
struct lock *lck;
|
|
||||||
struct dm_list *head;
|
|
||||||
int r = 1;
|
|
||||||
|
|
||||||
if (lockid < 0) {
|
|
||||||
DEBUGLOG("Not tracking unlock of lockid -1: %s, lockid=%d\n",
|
|
||||||
resource, lockid);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGLOG("Unlocking resource %s, lockid=%d\n", resource, lockid);
|
|
||||||
pthread_mutex_lock(&_lock_mutex);
|
|
||||||
pthread_cond_broadcast(&_lock_cond); /* wakeup waiters */
|
|
||||||
|
|
||||||
if (!(head = dm_hash_lookup(_locks, resource))) {
|
|
||||||
pthread_mutex_unlock(&_lock_mutex);
|
|
||||||
DEBUGLOG("Resource %s is not locked.\n", resource);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_list_iterate_items(lck, head)
|
|
||||||
if (lck->lockid == lockid) {
|
|
||||||
dm_list_del(&lck->list);
|
|
||||||
dm_free(lck);
|
|
||||||
r = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGLOG("Resource %s has wrong lockid %d.\n", resource, lockid);
|
|
||||||
out:
|
|
||||||
if (dm_list_empty(head)) {
|
|
||||||
//DEBUGLOG("Resource %s is no longer hashed (lockid=%d).\n", resource, lockid);
|
|
||||||
dm_hash_remove(_locks, resource);
|
|
||||||
dm_free(head);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&_lock_mutex);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _is_quorate(void)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_main_cluster_fd(void)
|
|
||||||
{
|
|
||||||
return listen_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
|
|
||||||
const char *csid,
|
|
||||||
struct local_client **new_client)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _cluster_send_message(const void *buf, int msglen,
|
|
||||||
const char *csid,
|
|
||||||
const char *errtext)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_cluster_name(char *buf, int buflen)
|
|
||||||
{
|
|
||||||
return dm_strncpy(buf, "localcluster", buflen) ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct cluster_ops _cluster_singlenode_ops = {
|
|
||||||
.name = "singlenode",
|
|
||||||
.cluster_init_completed = NULL,
|
|
||||||
.cluster_send_message = _cluster_send_message,
|
|
||||||
.name_from_csid = _name_from_csid,
|
|
||||||
.csid_from_name = _csid_from_name,
|
|
||||||
.get_num_nodes = _get_num_nodes,
|
|
||||||
.cluster_fd_callback = _cluster_fd_callback,
|
|
||||||
.get_main_cluster_fd = _get_main_cluster_fd,
|
|
||||||
.cluster_do_node_callback = _cluster_do_node_callback,
|
|
||||||
.is_quorate = _is_quorate,
|
|
||||||
.get_our_csid = _get_our_csid,
|
|
||||||
.add_up_node = _add_up_node,
|
|
||||||
.reread_config = NULL,
|
|
||||||
.cluster_closedown = _cluster_closedown,
|
|
||||||
.get_cluster_name = _get_cluster_name,
|
|
||||||
.sync_lock = _lock_resource,
|
|
||||||
.sync_unlock = _unlock_resource,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cluster_ops *init_singlenode_cluster(void)
|
|
||||||
{
|
|
||||||
if (!_init_cluster())
|
|
||||||
return &_cluster_singlenode_ops;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _CLVMD_H
|
|
||||||
#define _CLVMD_H
|
|
||||||
|
|
||||||
#define CLVMD_MAJOR_VERSION 0
|
|
||||||
#define CLVMD_MINOR_VERSION 2
|
|
||||||
#define CLVMD_PATCH_VERSION 1
|
|
||||||
|
|
||||||
/* Default time (in seconds) we will wait for all remote commands to execute
|
|
||||||
before declaring them dead */
|
|
||||||
#define DEFAULT_CMD_TIMEOUT 60
|
|
||||||
|
|
||||||
/* One of these for each reply we get from command execution on a node */
|
|
||||||
struct node_reply {
|
|
||||||
char node[MAX_CLUSTER_MEMBER_NAME_LEN];
|
|
||||||
char *replymsg;
|
|
||||||
int status;
|
|
||||||
struct node_reply *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum {DEBUG_OFF, DEBUG_STDERR, DEBUG_SYSLOG} debug_t;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These exist for the use of local sockets only when we are
|
|
||||||
* collecting responses from all cluster nodes
|
|
||||||
*/
|
|
||||||
struct localsock_bits {
|
|
||||||
struct node_reply *replies;
|
|
||||||
int num_replies;
|
|
||||||
int expected_replies;
|
|
||||||
time_t sent_time; /* So we can check for timeouts */
|
|
||||||
int in_progress; /* Only execute one cmd at a time per client */
|
|
||||||
int sent_out; /* Flag to indicate that a command was sent
|
|
||||||
to remote nodes */
|
|
||||||
void *private; /* Private area for command processor use */
|
|
||||||
void *cmd; /* Whole command as passed down local socket */
|
|
||||||
int cmd_len; /* Length of above */
|
|
||||||
int pipe; /* Pipe to send PRE completion status down */
|
|
||||||
int finished; /* Flag to tell subthread to exit */
|
|
||||||
int all_success; /* Set to 0 if any node (or the pre_command)
|
|
||||||
failed */
|
|
||||||
int cleanup_needed; /* helper for cleanup_zombie */
|
|
||||||
struct local_client *pipe_client;
|
|
||||||
pthread_t threadid;
|
|
||||||
enum { PRE_COMMAND, POST_COMMAND } state;
|
|
||||||
pthread_mutex_t mutex; /* Main thread and worker synchronisation */
|
|
||||||
pthread_cond_t cond;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Entries for PIPE clients */
|
|
||||||
struct pipe_bits {
|
|
||||||
struct local_client *client; /* Actual (localsock) client */
|
|
||||||
pthread_t threadid; /* Our own copy of the thread id */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Entries for Network socket clients */
|
|
||||||
struct netsock_bits {
|
|
||||||
void *private;
|
|
||||||
int flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef int (*fd_callback_t) (struct local_client * fd, char *buf, int len,
|
|
||||||
const char *csid,
|
|
||||||
struct local_client ** new_client);
|
|
||||||
|
|
||||||
/* One of these for each fd we are listening on */
|
|
||||||
struct local_client {
|
|
||||||
int fd;
|
|
||||||
enum { CLUSTER_MAIN_SOCK, CLUSTER_DATA_SOCK, LOCAL_RENDEZVOUS,
|
|
||||||
LOCAL_SOCK, THREAD_PIPE, CLUSTER_INTERNAL } type;
|
|
||||||
struct local_client *next;
|
|
||||||
unsigned short xid;
|
|
||||||
fd_callback_t callback;
|
|
||||||
uint8_t removeme;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct localsock_bits localsock;
|
|
||||||
struct pipe_bits pipe;
|
|
||||||
struct netsock_bits net;
|
|
||||||
} bits;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DEBUGLOG(fmt, args...) debuglog(fmt, ## args)
|
|
||||||
|
|
||||||
#ifndef max
|
|
||||||
#define max(a,b) ((a)>(b)?(a):(b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The real command processor is in clvmd-command.c */
|
|
||||||
extern int do_command(struct local_client *client, struct clvm_header *msg,
|
|
||||||
int msglen, char **buf, int buflen, int *retlen);
|
|
||||||
|
|
||||||
/* Pre and post command routines are called only on the local node */
|
|
||||||
extern int do_pre_command(struct local_client *client);
|
|
||||||
extern int do_post_command(struct local_client *client);
|
|
||||||
extern void cmd_client_cleanup(struct local_client *client);
|
|
||||||
extern int add_client(struct local_client *new_client);
|
|
||||||
|
|
||||||
extern void clvmd_cluster_init_completed(void);
|
|
||||||
extern void process_message(struct local_client *client, char *buf,
|
|
||||||
int len, const char *csid);
|
|
||||||
extern void debuglog(const char *fmt, ... )
|
|
||||||
__attribute__ ((format(printf, 1, 2)));
|
|
||||||
|
|
||||||
void clvmd_set_debug(debug_t new_de);
|
|
||||||
debug_t clvmd_get_debug(void);
|
|
||||||
int clvmd_get_foreground(void);
|
|
||||||
|
|
||||||
int sync_lock(const char *resource, int mode, int flags, int *lockid);
|
|
||||||
int sync_unlock(const char *resource, int lockid);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,939 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
* Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "clvmd-common.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include "clvm.h"
|
|
||||||
#include "clvmd-comms.h"
|
|
||||||
#include "clvmd.h"
|
|
||||||
#include "lvm-functions.h"
|
|
||||||
|
|
||||||
/* LVM2 headers */
|
|
||||||
#include "toolcontext.h"
|
|
||||||
#include "lvmcache.h"
|
|
||||||
#include "lvm-globals.h"
|
|
||||||
#include "activate.h"
|
|
||||||
#include "archiver.h"
|
|
||||||
#include "memlock.h"
|
|
||||||
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
static struct cmd_context *cmd = NULL;
|
|
||||||
static struct dm_hash_table *lv_hash = NULL;
|
|
||||||
static pthread_mutex_t lv_hash_lock;
|
|
||||||
static pthread_mutex_t lvm_lock;
|
|
||||||
static char last_error[1024];
|
|
||||||
|
|
||||||
struct lv_info {
|
|
||||||
int lock_id;
|
|
||||||
int lock_mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *decode_full_locking_cmd(uint32_t cmdl)
|
|
||||||
{
|
|
||||||
static char buf[128];
|
|
||||||
const char *type;
|
|
||||||
const char *scope;
|
|
||||||
const char *command;
|
|
||||||
|
|
||||||
switch (cmdl & LCK_TYPE_MASK) {
|
|
||||||
case LCK_NULL:
|
|
||||||
type = "NULL";
|
|
||||||
break;
|
|
||||||
case LCK_READ:
|
|
||||||
type = "READ";
|
|
||||||
break;
|
|
||||||
case LCK_PREAD:
|
|
||||||
type = "PREAD";
|
|
||||||
break;
|
|
||||||
case LCK_WRITE:
|
|
||||||
type = "WRITE";
|
|
||||||
break;
|
|
||||||
case LCK_EXCL:
|
|
||||||
type = "EXCL";
|
|
||||||
break;
|
|
||||||
case LCK_UNLOCK:
|
|
||||||
type = "UNLOCK";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
type = "unknown";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (cmdl & LCK_SCOPE_MASK) {
|
|
||||||
case LCK_VG:
|
|
||||||
scope = "VG";
|
|
||||||
command = "LCK_VG";
|
|
||||||
break;
|
|
||||||
case LCK_LV:
|
|
||||||
scope = "LV";
|
|
||||||
switch (cmdl & LCK_MASK) {
|
|
||||||
case LCK_LV_EXCLUSIVE & LCK_MASK:
|
|
||||||
command = "LCK_LV_EXCLUSIVE";
|
|
||||||
break;
|
|
||||||
case LCK_LV_SUSPEND & LCK_MASK:
|
|
||||||
command = "LCK_LV_SUSPEND";
|
|
||||||
break;
|
|
||||||
case LCK_LV_RESUME & LCK_MASK:
|
|
||||||
command = "LCK_LV_RESUME";
|
|
||||||
break;
|
|
||||||
case LCK_LV_ACTIVATE & LCK_MASK:
|
|
||||||
command = "LCK_LV_ACTIVATE";
|
|
||||||
break;
|
|
||||||
case LCK_LV_DEACTIVATE & LCK_MASK:
|
|
||||||
command = "LCK_LV_DEACTIVATE";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
command = "unknown";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
scope = "unknown";
|
|
||||||
command = "unknown";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(buf, "0x%x %s (%s|%s%s%s%s%s)", cmdl, command, type, scope,
|
|
||||||
cmdl & LCK_NONBLOCK ? "|NONBLOCK" : "",
|
|
||||||
cmdl & LCK_HOLD ? "|HOLD" : "",
|
|
||||||
cmdl & LCK_CLUSTER_VG ? "|CLUSTER_VG" : "",
|
|
||||||
cmdl & LCK_CACHE ? "|CACHE" : "");
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only processes 8 bits: excludes LCK_CACHE.
|
|
||||||
*/
|
|
||||||
static const char *decode_locking_cmd(unsigned char cmdl)
|
|
||||||
{
|
|
||||||
return decode_full_locking_cmd((uint32_t) cmdl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *decode_flags(unsigned char flags)
|
|
||||||
{
|
|
||||||
static char buf[128];
|
|
||||||
int len;
|
|
||||||
|
|
||||||
len = sprintf(buf, "0x%x ( %s%s%s%s%s%s%s%s)", flags,
|
|
||||||
flags & LCK_PARTIAL_MODE ? "PARTIAL_MODE|" : "",
|
|
||||||
flags & LCK_MIRROR_NOSYNC_MODE ? "MIRROR_NOSYNC|" : "",
|
|
||||||
flags & LCK_DMEVENTD_MONITOR_MODE ? "DMEVENTD_MONITOR|" : "",
|
|
||||||
flags & LCK_ORIGIN_ONLY_MODE ? "ORIGIN_ONLY|" : "",
|
|
||||||
flags & LCK_TEST_MODE ? "TEST|" : "",
|
|
||||||
flags & LCK_CONVERT_MODE ? "CONVERT|" : "",
|
|
||||||
flags & LCK_DMEVENTD_MONITOR_IGNORE ? "DMEVENTD_MONITOR_IGNORE|" : "",
|
|
||||||
flags & LCK_REVERT_MODE ? "REVERT|" : "");
|
|
||||||
|
|
||||||
if (len > 1)
|
|
||||||
buf[len - 2] = ' ';
|
|
||||||
else
|
|
||||||
buf[0] = '\0';
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *get_last_lvm_error(void)
|
|
||||||
{
|
|
||||||
return last_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Hash lock info helpers
|
|
||||||
*/
|
|
||||||
static struct lv_info *lookup_info(const char *resource)
|
|
||||||
{
|
|
||||||
struct lv_info *lvi;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lv_hash_lock);
|
|
||||||
lvi = dm_hash_lookup(lv_hash, resource);
|
|
||||||
pthread_mutex_unlock(&lv_hash_lock);
|
|
||||||
|
|
||||||
return lvi;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int insert_info(const char *resource, struct lv_info *lvi)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lv_hash_lock);
|
|
||||||
ret = dm_hash_insert(lv_hash, resource, lvi);
|
|
||||||
pthread_mutex_unlock(&lv_hash_lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void remove_info(const char *resource)
|
|
||||||
{
|
|
||||||
int num_open;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lv_hash_lock);
|
|
||||||
dm_hash_remove(lv_hash, resource);
|
|
||||||
|
|
||||||
/* When last lock is remove, validate there are not left opened devices */
|
|
||||||
if (!dm_hash_get_first(lv_hash)) {
|
|
||||||
if (critical_section())
|
|
||||||
log_error(INTERNAL_ERROR "No volumes are locked however clvmd is in activation mode critical section.");
|
|
||||||
if ((num_open = dev_cache_check_for_open_devices()))
|
|
||||||
log_error(INTERNAL_ERROR "No volumes are locked however %d devices are still open.", num_open);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&lv_hash_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the mode a lock is currently held at (or -1 if not held)
|
|
||||||
*/
|
|
||||||
static int get_current_lock(char *resource)
|
|
||||||
{
|
|
||||||
struct lv_info *lvi;
|
|
||||||
|
|
||||||
if ((lvi = lookup_info(resource)))
|
|
||||||
return lvi->lock_mode;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void init_lvhash(void)
|
|
||||||
{
|
|
||||||
/* Create hash table for keeping LV locks & status */
|
|
||||||
lv_hash = dm_hash_create(1024);
|
|
||||||
pthread_mutex_init(&lv_hash_lock, NULL);
|
|
||||||
pthread_mutex_init(&lvm_lock, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called at shutdown to tidy the lockspace */
|
|
||||||
void destroy_lvhash(void)
|
|
||||||
{
|
|
||||||
struct dm_hash_node *v;
|
|
||||||
struct lv_info *lvi;
|
|
||||||
char *resource;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lv_hash_lock);
|
|
||||||
|
|
||||||
dm_hash_iterate(v, lv_hash) {
|
|
||||||
lvi = dm_hash_get_data(lv_hash, v);
|
|
||||||
resource = dm_hash_get_key(lv_hash, v);
|
|
||||||
|
|
||||||
if ((status = sync_unlock(resource, lvi->lock_id)))
|
|
||||||
DEBUGLOG("unlock_all. unlock failed(%d): %s\n",
|
|
||||||
status, strerror(errno));
|
|
||||||
dm_free(lvi);
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_hash_destroy(lv_hash);
|
|
||||||
lv_hash = NULL;
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&lv_hash_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gets a real lock and keeps the info in the hash table */
|
|
||||||
static int hold_lock(char *resource, int mode, int flags)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
int saved_errno;
|
|
||||||
struct lv_info *lvi;
|
|
||||||
|
|
||||||
/* Mask off invalid options */
|
|
||||||
flags &= LCKF_NOQUEUE | LCKF_CONVERT;
|
|
||||||
|
|
||||||
lvi = lookup_info(resource);
|
|
||||||
|
|
||||||
if (lvi) {
|
|
||||||
if (lvi->lock_mode == mode) {
|
|
||||||
DEBUGLOG("hold_lock, lock mode %d already held\n",
|
|
||||||
mode);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if ((lvi->lock_mode == LCK_EXCL) && (mode == LCK_WRITE)) {
|
|
||||||
DEBUGLOG("hold_lock, lock already held LCK_EXCL, "
|
|
||||||
"ignoring LCK_WRITE request\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only allow explicit conversions */
|
|
||||||
if (lvi && !(flags & LCKF_CONVERT)) {
|
|
||||||
errno = EBUSY;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (lvi) {
|
|
||||||
/* Already exists - convert it */
|
|
||||||
status = sync_lock(resource, mode, flags, &lvi->lock_id);
|
|
||||||
saved_errno = errno;
|
|
||||||
if (!status)
|
|
||||||
lvi->lock_mode = mode;
|
|
||||||
else
|
|
||||||
DEBUGLOG("hold_lock. convert to %d failed: %s\n", mode,
|
|
||||||
strerror(errno));
|
|
||||||
errno = saved_errno;
|
|
||||||
} else {
|
|
||||||
if (!(lvi = dm_malloc(sizeof(struct lv_info)))) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
lvi->lock_mode = mode;
|
|
||||||
lvi->lock_id = 0;
|
|
||||||
status = sync_lock(resource, mode, flags & ~LCKF_CONVERT, &lvi->lock_id);
|
|
||||||
saved_errno = errno;
|
|
||||||
if (status) {
|
|
||||||
dm_free(lvi);
|
|
||||||
DEBUGLOG("hold_lock. lock at %d failed: %s\n", mode,
|
|
||||||
strerror(errno));
|
|
||||||
} else
|
|
||||||
if (!insert_info(resource, lvi)) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = saved_errno;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unlock and remove it from the hash table */
|
|
||||||
static int hold_unlock(char *resource)
|
|
||||||
{
|
|
||||||
struct lv_info *lvi;
|
|
||||||
int status;
|
|
||||||
int saved_errno;
|
|
||||||
|
|
||||||
if (!(lvi = lookup_info(resource))) {
|
|
||||||
DEBUGLOG("hold_unlock, lock not already held\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = sync_unlock(resource, lvi->lock_id);
|
|
||||||
saved_errno = errno;
|
|
||||||
if (!status) {
|
|
||||||
remove_info(resource);
|
|
||||||
dm_free(lvi);
|
|
||||||
} else {
|
|
||||||
DEBUGLOG("hold_unlock. unlock failed(%d): %s\n", status,
|
|
||||||
strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = saved_errno;
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Watch the return codes here.
|
|
||||||
liblvm API functions return 1(true) for success, 0(false) for failure and don't set errno.
|
|
||||||
libdlm API functions return 0 for success, -1 for failure and do set errno.
|
|
||||||
These functions here return 0 for success or >0 for failure (where the retcode is errno)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Activate LV exclusive or non-exclusive */
|
|
||||||
static int do_activate_lv(char *resource, unsigned char command, unsigned char lock_flags, int mode)
|
|
||||||
{
|
|
||||||
int oldmode;
|
|
||||||
int status;
|
|
||||||
int activate_lv;
|
|
||||||
int exclusive = 0;
|
|
||||||
struct lvinfo lvi;
|
|
||||||
|
|
||||||
/* Is it already open ? */
|
|
||||||
oldmode = get_current_lock(resource);
|
|
||||||
if (oldmode == mode && (command & LCK_CLUSTER_VG)) {
|
|
||||||
DEBUGLOG("do_activate_lv, lock already held at %d\n", oldmode);
|
|
||||||
return 0; /* Nothing to do */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Does the config file want us to activate this LV ? */
|
|
||||||
if (!lv_activation_filter(cmd, resource, &activate_lv, NULL))
|
|
||||||
return EIO;
|
|
||||||
|
|
||||||
if (!activate_lv)
|
|
||||||
return 0; /* Success, we did nothing! */
|
|
||||||
|
|
||||||
/* Do we need to activate exclusively? */
|
|
||||||
if ((activate_lv == 2) || (mode == LCK_EXCL)) {
|
|
||||||
exclusive = 1;
|
|
||||||
mode = LCK_EXCL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to get the lock if it's a clustered volume group.
|
|
||||||
* Use lock conversion only if requested, to prevent implicit conversion
|
|
||||||
* of exclusive lock to shared one during activation.
|
|
||||||
*/
|
|
||||||
if (!test_mode() && command & LCK_CLUSTER_VG) {
|
|
||||||
status = hold_lock(resource, mode, LCKF_NOQUEUE | ((lock_flags & LCK_CONVERT_MODE) ? LCKF_CONVERT:0));
|
|
||||||
if (status) {
|
|
||||||
/* Return an LVM-sensible error for this.
|
|
||||||
* Forcing EIO makes the upper level return this text
|
|
||||||
* rather than the strerror text for EAGAIN.
|
|
||||||
*/
|
|
||||||
if (errno == EAGAIN) {
|
|
||||||
sprintf(last_error, "Volume is busy on another node");
|
|
||||||
errno = EIO;
|
|
||||||
}
|
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it's suspended then resume it */
|
|
||||||
if (!lv_info_by_lvid(cmd, resource, 0, &lvi, 0, 0))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (lvi.suspended) {
|
|
||||||
critical_section_inc(cmd, "resuming");
|
|
||||||
if (!lv_resume(cmd, resource, 0, NULL)) {
|
|
||||||
critical_section_dec(cmd, "resumed");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now activate it */
|
|
||||||
if (!lv_activate(cmd, resource, exclusive, 0, 0, NULL))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (!test_mode() && (oldmode == -1 || oldmode != mode))
|
|
||||||
(void)hold_unlock(resource);
|
|
||||||
return EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume the LV if it was active */
|
|
||||||
static int do_resume_lv(char *resource, unsigned char command, unsigned char lock_flags)
|
|
||||||
{
|
|
||||||
int oldmode, origin_only, exclusive, revert;
|
|
||||||
|
|
||||||
/* Is it open ? */
|
|
||||||
oldmode = get_current_lock(resource);
|
|
||||||
if (oldmode == -1 && (command & LCK_CLUSTER_VG)) {
|
|
||||||
DEBUGLOG("do_resume_lv, lock not already held\n");
|
|
||||||
return 0; /* We don't need to do anything */
|
|
||||||
}
|
|
||||||
origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
|
|
||||||
exclusive = (oldmode == LCK_EXCL) ? 1 : 0;
|
|
||||||
revert = (lock_flags & LCK_REVERT_MODE) ? 1 : 0;
|
|
||||||
|
|
||||||
if (!lv_resume_if_active(cmd, resource, origin_only, exclusive, revert, NULL))
|
|
||||||
return EIO;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Suspend the device if active */
|
|
||||||
static int do_suspend_lv(char *resource, unsigned char command, unsigned char lock_flags)
|
|
||||||
{
|
|
||||||
int oldmode;
|
|
||||||
unsigned origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
|
|
||||||
unsigned exclusive;
|
|
||||||
|
|
||||||
/* Is it open ? */
|
|
||||||
oldmode = get_current_lock(resource);
|
|
||||||
if (oldmode == -1 && (command & LCK_CLUSTER_VG)) {
|
|
||||||
DEBUGLOG("do_suspend_lv, lock not already held\n");
|
|
||||||
return 0; /* Not active, so it's OK */
|
|
||||||
}
|
|
||||||
|
|
||||||
exclusive = (oldmode == LCK_EXCL) ? 1 : 0;
|
|
||||||
|
|
||||||
/* Always call lv_suspend to read commited and precommited data */
|
|
||||||
if (!lv_suspend_if_active(cmd, resource, origin_only, exclusive, NULL, NULL))
|
|
||||||
return EIO;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int do_deactivate_lv(char *resource, unsigned char command, unsigned char lock_flags)
|
|
||||||
{
|
|
||||||
int oldmode;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
/* Is it open ? */
|
|
||||||
oldmode = get_current_lock(resource);
|
|
||||||
if (oldmode == -1 && (command & LCK_CLUSTER_VG)) {
|
|
||||||
DEBUGLOG("do_deactivate_lock, lock not already held\n");
|
|
||||||
return 0; /* We don't need to do anything */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lv_deactivate(cmd, resource, NULL))
|
|
||||||
return EIO;
|
|
||||||
|
|
||||||
if (!test_mode() && command & LCK_CLUSTER_VG) {
|
|
||||||
status = hold_unlock(resource);
|
|
||||||
if (status)
|
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *do_lock_query(char *resource)
|
|
||||||
{
|
|
||||||
int mode;
|
|
||||||
const char *type;
|
|
||||||
|
|
||||||
mode = get_current_lock(resource);
|
|
||||||
switch (mode) {
|
|
||||||
case LCK_NULL: type = "NL"; break;
|
|
||||||
case LCK_READ: type = "CR"; break;
|
|
||||||
case LCK_PREAD:type = "PR"; break;
|
|
||||||
case LCK_WRITE:type = "PW"; break;
|
|
||||||
case LCK_EXCL: type = "EX"; break;
|
|
||||||
default: type = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGLOG("do_lock_query: resource '%s', mode %i (%s)\n", resource, mode, type ?: "--");
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is the LOCK_LV part that happens on all nodes in the cluster -
|
|
||||||
it is responsible for the interaction with device-mapper and LVM */
|
|
||||||
int do_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
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->initialized.config || config_files_changed(cmd)) {
|
|
||||||
/* Reinitialise various settings inc. logging, filters */
|
|
||||||
if (do_refresh_cache()) {
|
|
||||||
log_error("Updated config file invalid. Aborting.");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lvm_lock);
|
|
||||||
init_test((lock_flags & LCK_TEST_MODE) ? 1 : 0);
|
|
||||||
|
|
||||||
if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
|
|
||||||
init_mirror_in_sync(1);
|
|
||||||
|
|
||||||
if (lock_flags & LCK_DMEVENTD_MONITOR_IGNORE)
|
|
||||||
init_dmeventd_monitor(DMEVENTD_MONITOR_IGNORE);
|
|
||||||
else {
|
|
||||||
if (lock_flags & LCK_DMEVENTD_MONITOR_MODE)
|
|
||||||
init_dmeventd_monitor(1);
|
|
||||||
else
|
|
||||||
init_dmeventd_monitor(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd->partial_activation = (lock_flags & LCK_PARTIAL_MODE) ? 1 : 0;
|
|
||||||
|
|
||||||
/* clvmd should never try to read suspended device */
|
|
||||||
init_ignore_suspended_devices(1);
|
|
||||||
|
|
||||||
switch (command & LCK_MASK) {
|
|
||||||
case LCK_LV_EXCLUSIVE:
|
|
||||||
status = do_activate_lv(resource, command, lock_flags, LCK_EXCL);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LCK_LV_SUSPEND:
|
|
||||||
status = do_suspend_lv(resource, command, lock_flags);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LCK_UNLOCK:
|
|
||||||
case LCK_LV_RESUME: /* if active */
|
|
||||||
status = do_resume_lv(resource, command, lock_flags);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LCK_LV_ACTIVATE:
|
|
||||||
status = do_activate_lv(resource, command, lock_flags, LCK_READ);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LCK_LV_DEACTIVATE:
|
|
||||||
status = do_deactivate_lv(resource, command, lock_flags);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
DEBUGLOG("Invalid LV command 0x%x\n", command);
|
|
||||||
status = EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
|
|
||||||
init_mirror_in_sync(0);
|
|
||||||
|
|
||||||
cmd->partial_activation = 0;
|
|
||||||
|
|
||||||
/* clean the pool for another command */
|
|
||||||
dm_pool_empty(cmd->mem);
|
|
||||||
init_test(0);
|
|
||||||
pthread_mutex_unlock(&lvm_lock);
|
|
||||||
|
|
||||||
DEBUGLOG("Command return is %d, critical_section is %d\n", status, critical_section());
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Functions to do on the local node only BEFORE the cluster-wide stuff above happens */
|
|
||||||
int pre_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
|
|
||||||
{
|
|
||||||
/* Nearly all the stuff happens cluster-wide. Apart from SUSPEND. Here we get the
|
|
||||||
lock out on this node (because we are the node modifying the metadata)
|
|
||||||
before suspending cluster-wide.
|
|
||||||
LCKF_CONVERT is used always, local node is going to modify metadata
|
|
||||||
*/
|
|
||||||
if ((command & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) == LCK_LV_SUSPEND &&
|
|
||||||
(command & LCK_CLUSTER_VG)) {
|
|
||||||
DEBUGLOG("pre_lock_lv: resource '%s', cmd = %s, flags = %s\n",
|
|
||||||
resource, decode_locking_cmd(command), decode_flags(lock_flags));
|
|
||||||
|
|
||||||
if (!(lock_flags & LCK_TEST_MODE) &&
|
|
||||||
hold_lock(resource, LCK_WRITE, LCKF_NOQUEUE | LCKF_CONVERT))
|
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Functions to do on the local node only AFTER the cluster-wide stuff above happens */
|
|
||||||
int post_lock_lv(unsigned char command, unsigned char lock_flags,
|
|
||||||
char *resource)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
unsigned origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
|
|
||||||
|
|
||||||
/* Opposite of above, done on resume after a metadata update */
|
|
||||||
if ((command & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) == LCK_LV_RESUME &&
|
|
||||||
(command & LCK_CLUSTER_VG)) {
|
|
||||||
int oldmode;
|
|
||||||
|
|
||||||
DEBUGLOG("post_lock_lv: resource '%s', cmd = %s, flags = %s\n",
|
|
||||||
resource, decode_locking_cmd(command), decode_flags(lock_flags));
|
|
||||||
|
|
||||||
/* If the lock state is PW then restore it to what it was */
|
|
||||||
oldmode = get_current_lock(resource);
|
|
||||||
if (oldmode == LCK_WRITE) {
|
|
||||||
struct lvinfo lvi;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lvm_lock);
|
|
||||||
status = lv_info_by_lvid(cmd, resource, origin_only, &lvi, 0, 0);
|
|
||||||
pthread_mutex_unlock(&lvm_lock);
|
|
||||||
if (!status)
|
|
||||||
return EIO;
|
|
||||||
|
|
||||||
if (!(lock_flags & LCK_TEST_MODE)) {
|
|
||||||
if (lvi.exists) {
|
|
||||||
if (hold_lock(resource, LCK_READ, LCKF_CONVERT))
|
|
||||||
return errno;
|
|
||||||
} else if (hold_unlock(resource))
|
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if a VG is in use by LVM1 so we don't stomp on it */
|
|
||||||
int do_check_lvm1(const char *vgname)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
|
|
||||||
status = check_lvm1_vg_inactive(cmd, vgname);
|
|
||||||
|
|
||||||
return status == 1 ? 0 : EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
int do_refresh_cache(void)
|
|
||||||
{
|
|
||||||
DEBUGLOG("Refreshing context\n");
|
|
||||||
log_notice("Refreshing context");
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lvm_lock);
|
|
||||||
|
|
||||||
if (!refresh_toolcontext(cmd)) {
|
|
||||||
pthread_mutex_unlock(&lvm_lock);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
init_full_scan_done(0);
|
|
||||||
init_ignore_suspended_devices(1);
|
|
||||||
lvmcache_force_next_label_scan();
|
|
||||||
lvmcache_label_scan(cmd);
|
|
||||||
dm_pool_empty(cmd->mem);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&lvm_lock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle VG lock - drop metadata or update lvmcache state
|
|
||||||
*/
|
|
||||||
void do_lock_vg(unsigned char command, unsigned char lock_flags, char *resource)
|
|
||||||
{
|
|
||||||
uint32_t lock_cmd = command;
|
|
||||||
char *vgname = resource + 2;
|
|
||||||
|
|
||||||
lock_cmd &= (LCK_SCOPE_MASK | LCK_TYPE_MASK | LCK_HOLD);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if LCK_CACHE should be set. All P_ locks except # are cache related.
|
|
||||||
*/
|
|
||||||
if (strncmp(resource, "P_#", 3) && !strncmp(resource, "P_", 2))
|
|
||||||
lock_cmd |= LCK_CACHE;
|
|
||||||
|
|
||||||
DEBUGLOG("do_lock_vg: resource '%s', cmd = %s, flags = %s, critical_section = %d\n",
|
|
||||||
resource, decode_full_locking_cmd(lock_cmd), decode_flags(lock_flags), critical_section());
|
|
||||||
|
|
||||||
/* P_#global causes a full cache refresh */
|
|
||||||
if (!strcmp(resource, "P_" VG_GLOBAL)) {
|
|
||||||
do_refresh_cache();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lvm_lock);
|
|
||||||
init_test((lock_flags & LCK_TEST_MODE) ? 1 : 0);
|
|
||||||
|
|
||||||
switch (lock_cmd) {
|
|
||||||
case LCK_VG_COMMIT:
|
|
||||||
DEBUGLOG("vg_commit notification for VG %s\n", vgname);
|
|
||||||
lvmcache_commit_metadata(vgname);
|
|
||||||
break;
|
|
||||||
case LCK_VG_REVERT:
|
|
||||||
DEBUGLOG("vg_revert notification for VG %s\n", vgname);
|
|
||||||
lvmcache_drop_metadata(vgname, 1);
|
|
||||||
break;
|
|
||||||
case LCK_VG_DROP_CACHE:
|
|
||||||
default:
|
|
||||||
DEBUGLOG("Invalidating cached metadata for VG %s\n", vgname);
|
|
||||||
lvmcache_drop_metadata(vgname, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
init_test(0);
|
|
||||||
pthread_mutex_unlock(&lvm_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ideally, clvmd should be started before any LVs are active
|
|
||||||
* but this may not be the case...
|
|
||||||
* I suppose this also comes in handy if clvmd crashes, not that it would!
|
|
||||||
*/
|
|
||||||
static int get_initial_state(struct dm_hash_table *excl_uuid)
|
|
||||||
{
|
|
||||||
int lock_mode;
|
|
||||||
char lv[65], vg[65], flags[26], vg_flags[26]; /* with space for '\0' */
|
|
||||||
char uuid[65];
|
|
||||||
char line[255];
|
|
||||||
char *lvs_cmd;
|
|
||||||
const char *lvm_binary = getenv("LVM_BINARY") ? : LVM_PATH;
|
|
||||||
FILE *lvs;
|
|
||||||
|
|
||||||
if (dm_asprintf(&lvs_cmd, "%s lvs --config 'log{command_names=0 prefix=\"\"}' "
|
|
||||||
"--nolocking --noheadings -o vg_uuid,lv_uuid,lv_attr,vg_attr",
|
|
||||||
lvm_binary) < 0)
|
|
||||||
return_0;
|
|
||||||
|
|
||||||
/* FIXME: Maybe link and use liblvm2cmd directly instead of fork */
|
|
||||||
if (!(lvs = popen(lvs_cmd, "r"))) {
|
|
||||||
dm_free(lvs_cmd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), lvs)) {
|
|
||||||
if (sscanf(line, "%64s %64s %25s %25s\n", vg, lv, flags, vg_flags) == 4) {
|
|
||||||
|
|
||||||
/* States: s:suspended a:active S:dropped snapshot I:invalid snapshot */
|
|
||||||
if (strlen(vg) == 38 && /* is is a valid UUID ? */
|
|
||||||
(flags[4] == 'a' || flags[4] == 's') && /* is it active or suspended? */
|
|
||||||
vg_flags[5] == 'c') { /* is it clustered ? */
|
|
||||||
/* Convert hyphen-separated UUIDs into one */
|
|
||||||
memcpy(&uuid[0], &vg[0], 6);
|
|
||||||
memcpy(&uuid[6], &vg[7], 4);
|
|
||||||
memcpy(&uuid[10], &vg[12], 4);
|
|
||||||
memcpy(&uuid[14], &vg[17], 4);
|
|
||||||
memcpy(&uuid[18], &vg[22], 4);
|
|
||||||
memcpy(&uuid[22], &vg[27], 4);
|
|
||||||
memcpy(&uuid[26], &vg[32], 6);
|
|
||||||
memcpy(&uuid[32], &lv[0], 6);
|
|
||||||
memcpy(&uuid[38], &lv[7], 4);
|
|
||||||
memcpy(&uuid[42], &lv[12], 4);
|
|
||||||
memcpy(&uuid[46], &lv[17], 4);
|
|
||||||
memcpy(&uuid[50], &lv[22], 4);
|
|
||||||
memcpy(&uuid[54], &lv[27], 4);
|
|
||||||
memcpy(&uuid[58], &lv[32], 6);
|
|
||||||
uuid[64] = '\0';
|
|
||||||
|
|
||||||
/* Look for this lock in the list of EX locks
|
|
||||||
we were passed on the command-line */
|
|
||||||
lock_mode = (dm_hash_lookup(excl_uuid, uuid)) ?
|
|
||||||
LCK_EXCL : LCK_READ;
|
|
||||||
|
|
||||||
DEBUGLOG("getting initial lock for %s\n", uuid);
|
|
||||||
if (hold_lock(uuid, lock_mode, LCKF_NOQUEUE))
|
|
||||||
DEBUGLOG("Failed to hold lock %s\n", uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pclose(lvs))
|
|
||||||
DEBUGLOG("lvs pclose failed: %s\n", strerror(errno));
|
|
||||||
|
|
||||||
dm_free(lvs_cmd);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lvm2_log_fn(int level, const char *file, int line, int dm_errno,
|
|
||||||
const char *message)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Send messages to the normal LVM2 logging system too,
|
|
||||||
so we get debug output when it's asked for.
|
|
||||||
We need to NULL the function ptr otherwise it will just call
|
|
||||||
back into here! */
|
|
||||||
init_log_fn(NULL);
|
|
||||||
print_log(level, file, line, dm_errno, "%s", message);
|
|
||||||
init_log_fn(lvm2_log_fn);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ignore non-error messages, but store the latest one for returning
|
|
||||||
* to the user.
|
|
||||||
*/
|
|
||||||
if (level != _LOG_ERR && level != _LOG_FATAL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
strncpy(last_error, message, sizeof(last_error));
|
|
||||||
last_error[sizeof(last_error)-1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This checks some basic cluster-LVM configuration stuff */
|
|
||||||
static void check_config(void)
|
|
||||||
{
|
|
||||||
int locking_type;
|
|
||||||
|
|
||||||
locking_type = find_config_tree_int(cmd, global_locking_type_CFG, NULL);
|
|
||||||
|
|
||||||
if (locking_type == 3) /* compiled-in cluster support */
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (locking_type == 2) { /* External library, check name */
|
|
||||||
const char *libname;
|
|
||||||
|
|
||||||
libname = find_config_tree_str(cmd, global_locking_library_CFG, NULL);
|
|
||||||
if (libname && strstr(libname, "liblvm2clusterlock.so"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
log_error("Incorrect LVM locking library specified in lvm.conf, cluster operations may not work.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log_error("locking_type not set correctly in lvm.conf, cluster operations will not work.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Backups up the LVM metadata if it's changed */
|
|
||||||
void lvm_do_backup(const char *vgname)
|
|
||||||
{
|
|
||||||
struct volume_group * vg;
|
|
||||||
int consistent = 0;
|
|
||||||
|
|
||||||
DEBUGLOG("Triggering backup of VG metadata for %s.\n", vgname);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lvm_lock);
|
|
||||||
|
|
||||||
vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, WARN_PV_READ, &consistent);
|
|
||||||
|
|
||||||
if (vg && consistent)
|
|
||||||
check_current_backup(vg);
|
|
||||||
else
|
|
||||||
log_error("Error backing up metadata, can't find VG for group %s", vgname);
|
|
||||||
|
|
||||||
release_vg(vg);
|
|
||||||
dm_pool_empty(cmd->mem);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&lvm_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dm_hash_node *get_next_excl_lock(struct dm_hash_node *v, char **name)
|
|
||||||
{
|
|
||||||
struct lv_info *lvi;
|
|
||||||
|
|
||||||
*name = NULL;
|
|
||||||
if (!v)
|
|
||||||
v = dm_hash_get_first(lv_hash);
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (v) {
|
|
||||||
lvi = dm_hash_get_data(lv_hash, v);
|
|
||||||
DEBUGLOG("Looking for EX locks. found %x mode %d\n", lvi->lock_id, lvi->lock_mode);
|
|
||||||
|
|
||||||
if (lvi->lock_mode == LCK_EXCL) {
|
|
||||||
*name = dm_hash_get_key(lv_hash, v);
|
|
||||||
}
|
|
||||||
v = dm_hash_get_next(lv_hash, v);
|
|
||||||
}
|
|
||||||
} while (v && !*name);
|
|
||||||
|
|
||||||
if (*name)
|
|
||||||
DEBUGLOG("returning EXclusive UUID %s\n", *name);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void lvm_do_fs_unlock(void)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&lvm_lock);
|
|
||||||
DEBUGLOG("Syncing device names\n");
|
|
||||||
fs_unlock();
|
|
||||||
pthread_mutex_unlock(&lvm_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called to initialise the LVM context of the daemon */
|
|
||||||
int init_clvm(struct dm_hash_table *excl_uuid)
|
|
||||||
{
|
|
||||||
/* Use LOG_DAEMON for syslog messages instead of LOG_USER */
|
|
||||||
init_syslog(LOG_DAEMON);
|
|
||||||
openlog("clvmd", LOG_PID, LOG_DAEMON);
|
|
||||||
|
|
||||||
/* Initialise already held locks */
|
|
||||||
if (!get_initial_state(excl_uuid))
|
|
||||||
log_error("Cannot load initial lock states.");
|
|
||||||
|
|
||||||
if (!udev_init_library_context())
|
|
||||||
stack;
|
|
||||||
|
|
||||||
if (!(cmd = create_toolcontext(1, NULL, 0, 1, 1, 1))) {
|
|
||||||
log_error("Failed to allocate command context");
|
|
||||||
udev_fin_library_context();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stored_errno()) {
|
|
||||||
destroy_toolcontext(cmd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd->cmd_line = "clvmd";
|
|
||||||
|
|
||||||
/* Check lvm.conf is setup for cluster-LVM */
|
|
||||||
check_config();
|
|
||||||
init_ignore_suspended_devices(1);
|
|
||||||
|
|
||||||
/* Trap log messages so we can pass them back to the user */
|
|
||||||
init_log_fn(lvm2_log_fn);
|
|
||||||
memlock_inc_daemon(cmd);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_lvm(void)
|
|
||||||
{
|
|
||||||
if (cmd) {
|
|
||||||
memlock_dec_daemon(cmd);
|
|
||||||
destroy_toolcontext(cmd);
|
|
||||||
udev_fin_library_context();
|
|
||||||
cmd = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
* Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Functions in lvm-functions.c */
|
|
||||||
|
|
||||||
#ifndef _LVM_FUNCTIONS_H
|
|
||||||
#define _LVM_FUNCTIONS_H
|
|
||||||
|
|
||||||
extern int pre_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
|
|
||||||
char *resource);
|
|
||||||
extern int do_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
|
|
||||||
char *resource);
|
|
||||||
extern const char *do_lock_query(char *resource);
|
|
||||||
extern int post_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
|
|
||||||
char *resource);
|
|
||||||
extern int do_check_lvm1(const char *vgname);
|
|
||||||
extern int do_refresh_cache(void);
|
|
||||||
extern int init_clvm(struct dm_hash_table *excl_uuid);
|
|
||||||
extern void destroy_lvm(void);
|
|
||||||
extern void init_lvhash(void);
|
|
||||||
extern void destroy_lvhash(void);
|
|
||||||
extern void lvm_do_backup(const char *vgname);
|
|
||||||
extern char *get_last_lvm_error(void);
|
|
||||||
extern void do_lock_vg(unsigned char command, unsigned char lock_flags,
|
|
||||||
char *resource);
|
|
||||||
extern struct dm_hash_node *get_next_excl_lock(struct dm_hash_node *v, char **name);
|
|
||||||
void lvm_do_fs_unlock(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,382 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
* Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* FIXME Remove duplicated functions from this file. */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Send a command to a running clvmd from the command-line
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "clvmd-common.h"
|
|
||||||
|
|
||||||
#include "clvm.h"
|
|
||||||
#include "refresh_clvmd.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
|
|
||||||
typedef struct lvm_response {
|
|
||||||
char node[255];
|
|
||||||
char *response;
|
|
||||||
int status;
|
|
||||||
int len;
|
|
||||||
} lvm_response_t;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This gets stuck at the start of memory we allocate so we
|
|
||||||
* can sanity-check it at deallocation time
|
|
||||||
*/
|
|
||||||
#define LVM_SIGNATURE 0x434C564D
|
|
||||||
|
|
||||||
static int _clvmd_sock = -1;
|
|
||||||
|
|
||||||
/* Open connection to the clvm daemon */
|
|
||||||
static int _open_local_sock(void)
|
|
||||||
{
|
|
||||||
int local_socket;
|
|
||||||
struct sockaddr_un sockaddr = { .sun_family = AF_UNIX };
|
|
||||||
|
|
||||||
if (!dm_strncpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(sockaddr.sun_path))) {
|
|
||||||
fprintf(stderr, "%s: clvmd socket name too long.", CLVMD_SOCKNAME);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open local socket */
|
|
||||||
if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
|
||||||
fprintf(stderr, "Local socket creation failed: %s", strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connect(local_socket,(struct sockaddr *) &sockaddr,
|
|
||||||
sizeof(sockaddr))) {
|
|
||||||
int saved_errno = errno;
|
|
||||||
|
|
||||||
fprintf(stderr, "connect() failed on local socket: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
if (close(local_socket))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
errno = saved_errno;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return local_socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send a request and return the status */
|
|
||||||
static int _send_request(const char *inbuf, int inlen, char **retbuf, int no_response)
|
|
||||||
{
|
|
||||||
char outbuf[PIPE_BUF];
|
|
||||||
struct clvm_header *outheader = (struct clvm_header *) outbuf;
|
|
||||||
int len;
|
|
||||||
unsigned off;
|
|
||||||
int buflen;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
/* Send it to CLVMD */
|
|
||||||
rewrite:
|
|
||||||
if ( (err = write(_clvmd_sock, inbuf, inlen)) != inlen) {
|
|
||||||
if (err == -1 && errno == EINTR)
|
|
||||||
goto rewrite;
|
|
||||||
fprintf(stderr, "Error writing data to clvmd: %s", strerror(errno));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (no_response)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
/* Get the response */
|
|
||||||
reread:
|
|
||||||
if ((len = read(_clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
|
|
||||||
if (errno == EINTR)
|
|
||||||
goto reread;
|
|
||||||
fprintf(stderr, "Error reading data from clvmd: %s", strerror(errno));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len == 0) {
|
|
||||||
fprintf(stderr, "EOF reading CLVMD");
|
|
||||||
errno = ENOTCONN;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate buffer */
|
|
||||||
buflen = len + outheader->arglen;
|
|
||||||
*retbuf = dm_malloc(buflen);
|
|
||||||
if (!*retbuf) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the header */
|
|
||||||
memcpy(*retbuf, outbuf, len);
|
|
||||||
outheader = (struct clvm_header *) *retbuf;
|
|
||||||
|
|
||||||
/* Read the returned values */
|
|
||||||
off = 1; /* we've already read the first byte */
|
|
||||||
while (off <= outheader->arglen && len > 0) {
|
|
||||||
len = read(_clvmd_sock, outheader->args + off,
|
|
||||||
buflen - off - offsetof(struct clvm_header, args));
|
|
||||||
if (len > 0)
|
|
||||||
off += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Was it an error ? */
|
|
||||||
if (outheader->status != 0) {
|
|
||||||
errno = outheader->status;
|
|
||||||
|
|
||||||
/* Only return an error here if there are no node-specific
|
|
||||||
errors present in the message that might have more detail */
|
|
||||||
if (!(outheader->flags & CLVMD_FLAG_NODEERRS)) {
|
|
||||||
fprintf(stderr, "cluster request failed: %s\n", strerror(errno));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Build the structure header and parse-out wildcard node names */
|
|
||||||
static void _build_header(struct clvm_header *head, int cmd, const char *node,
|
|
||||||
unsigned int len)
|
|
||||||
{
|
|
||||||
head->cmd = cmd;
|
|
||||||
head->status = 0;
|
|
||||||
head->flags = 0;
|
|
||||||
head->xid = 0;
|
|
||||||
head->clientid = 0;
|
|
||||||
if (len)
|
|
||||||
/* 1 byte is used from struct clvm_header.args[1], so -> len - 1 */
|
|
||||||
head->arglen = len - 1;
|
|
||||||
else {
|
|
||||||
head->arglen = 0;
|
|
||||||
*head->args = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Translate special node names.
|
|
||||||
*/
|
|
||||||
if (!node || !strcmp(node, NODE_ALL))
|
|
||||||
head->node[0] = '\0';
|
|
||||||
else if (!strcmp(node, NODE_LOCAL)) {
|
|
||||||
head->node[0] = '\0';
|
|
||||||
head->flags = CLVMD_FLAG_LOCAL;
|
|
||||||
} else
|
|
||||||
strcpy(head->node, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Send a message to a(or all) node(s) in the cluster and wait for replies
|
|
||||||
*/
|
|
||||||
static int _cluster_request(char cmd, const char *node, void *data, int len,
|
|
||||||
lvm_response_t ** response, int *num, int no_response)
|
|
||||||
{
|
|
||||||
char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1];
|
|
||||||
char *inptr;
|
|
||||||
char *retbuf = NULL;
|
|
||||||
int status;
|
|
||||||
int i;
|
|
||||||
int num_responses = 0;
|
|
||||||
struct clvm_header *head = (struct clvm_header *) outbuf;
|
|
||||||
lvm_response_t *rarray;
|
|
||||||
|
|
||||||
*num = 0;
|
|
||||||
|
|
||||||
if (_clvmd_sock == -1)
|
|
||||||
_clvmd_sock = _open_local_sock();
|
|
||||||
|
|
||||||
if (_clvmd_sock == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
_build_header(head, cmd, node, len);
|
|
||||||
if (len)
|
|
||||||
memcpy(head->node + strlen(head->node) + 1, data, len);
|
|
||||||
|
|
||||||
status = _send_request(outbuf, sizeof(struct clvm_header) +
|
|
||||||
strlen(head->node) + len, &retbuf, no_response);
|
|
||||||
if (!status || no_response)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* Count the number of responses we got */
|
|
||||||
head = (struct clvm_header *) retbuf;
|
|
||||||
inptr = head->args;
|
|
||||||
while (inptr[0]) {
|
|
||||||
num_responses++;
|
|
||||||
inptr += strlen(inptr) + 1;
|
|
||||||
inptr += sizeof(int);
|
|
||||||
inptr += strlen(inptr) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocate response array.
|
|
||||||
* With an extra pair of INTs on the front to sanity
|
|
||||||
* check the pointer when we are given it back to free
|
|
||||||
*/
|
|
||||||
*response = NULL;
|
|
||||||
if (!(rarray = dm_malloc(sizeof(lvm_response_t) * num_responses +
|
|
||||||
sizeof(int) * 2))) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
status = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unpack the response into an lvm_response_t array */
|
|
||||||
inptr = head->args;
|
|
||||||
i = 0;
|
|
||||||
while (inptr[0]) {
|
|
||||||
strcpy(rarray[i].node, inptr);
|
|
||||||
inptr += strlen(inptr) + 1;
|
|
||||||
|
|
||||||
memcpy(&rarray[i].status, inptr, sizeof(int));
|
|
||||||
inptr += sizeof(int);
|
|
||||||
|
|
||||||
rarray[i].response = dm_malloc(strlen(inptr) + 1);
|
|
||||||
if (rarray[i].response == NULL) {
|
|
||||||
/* Free up everything else and return error */
|
|
||||||
int j;
|
|
||||||
for (j = 0; j < i; j++)
|
|
||||||
dm_free(rarray[i].response);
|
|
||||||
dm_free(rarray);
|
|
||||||
errno = ENOMEM;
|
|
||||||
status = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
strcpy(rarray[i].response, inptr);
|
|
||||||
rarray[i].len = strlen(inptr);
|
|
||||||
inptr += strlen(inptr) + 1;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
*num = num_responses;
|
|
||||||
*response = rarray;
|
|
||||||
|
|
||||||
out:
|
|
||||||
dm_free(retbuf);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free reply array */
|
|
||||||
static int _cluster_free_request(lvm_response_t * response, int num)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
|
||||||
dm_free(response[i].response);
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_free(response);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int refresh_clvmd(int all_nodes)
|
|
||||||
{
|
|
||||||
int num_responses;
|
|
||||||
char args[1]; // No args really.
|
|
||||||
lvm_response_t *response = NULL;
|
|
||||||
int saved_errno;
|
|
||||||
int status;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
status = _cluster_request(CLVMD_CMD_REFRESH, all_nodes ? NODE_ALL : NODE_LOCAL, args, 0, &response, &num_responses, 0);
|
|
||||||
|
|
||||||
/* If any nodes were down then display them and return an error */
|
|
||||||
for (i = 0; i < num_responses; i++) {
|
|
||||||
if (response[i].status == EHOSTDOWN) {
|
|
||||||
fprintf(stderr, "clvmd not running on node %s",
|
|
||||||
response[i].node);
|
|
||||||
status = 0;
|
|
||||||
errno = response[i].status;
|
|
||||||
} else if (response[i].status) {
|
|
||||||
fprintf(stderr, "Error resetting node %s: %s",
|
|
||||||
response[i].node,
|
|
||||||
response[i].response[0] ?
|
|
||||||
response[i].response :
|
|
||||||
strerror(response[i].status));
|
|
||||||
status = 0;
|
|
||||||
errno = response[i].status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
saved_errno = errno;
|
|
||||||
_cluster_free_request(response, num_responses);
|
|
||||||
errno = saved_errno;
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
int restart_clvmd(int all_nodes)
|
|
||||||
{
|
|
||||||
int dummy, status;
|
|
||||||
|
|
||||||
status = _cluster_request(CLVMD_CMD_RESTART, all_nodes ? NODE_ALL : NODE_LOCAL, NULL, 0, NULL, &dummy, 1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: we cannot receive response, clvmd re-exec before it.
|
|
||||||
* but also should not close socket too early (the whole rq is dropped then).
|
|
||||||
* FIXME: This should be handled this way:
|
|
||||||
* - client waits for RESTART ack (and socket close)
|
|
||||||
* - server restarts
|
|
||||||
* - client checks that server is ready again (VERSION command?)
|
|
||||||
*/
|
|
||||||
usleep(500000);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
int debug_clvmd(int level, int clusterwide)
|
|
||||||
{
|
|
||||||
int num_responses;
|
|
||||||
char args[1];
|
|
||||||
const char *nodes;
|
|
||||||
lvm_response_t *response = NULL;
|
|
||||||
int saved_errno;
|
|
||||||
int status;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
args[0] = level;
|
|
||||||
if (clusterwide)
|
|
||||||
nodes = NODE_ALL;
|
|
||||||
else
|
|
||||||
nodes = NODE_LOCAL;
|
|
||||||
|
|
||||||
status = _cluster_request(CLVMD_CMD_SET_DEBUG, nodes, args, 1, &response, &num_responses, 0);
|
|
||||||
|
|
||||||
/* If any nodes were down then display them and return an error */
|
|
||||||
for (i = 0; i < num_responses; i++) {
|
|
||||||
if (response[i].status == EHOSTDOWN) {
|
|
||||||
fprintf(stderr, "clvmd not running on node %s",
|
|
||||||
response[i].node);
|
|
||||||
status = 0;
|
|
||||||
errno = response[i].status;
|
|
||||||
} else if (response[i].status) {
|
|
||||||
fprintf(stderr, "Error setting debug on node %s: %s",
|
|
||||||
response[i].node,
|
|
||||||
response[i].response[0] ?
|
|
||||||
response[i].response :
|
|
||||||
strerror(response[i].status));
|
|
||||||
status = 0;
|
|
||||||
errno = response[i].status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
saved_errno = errno;
|
|
||||||
_cluster_free_request(response, num_responses);
|
|
||||||
errno = saved_errno;
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
int refresh_clvmd(int all_nodes);
|
|
||||||
int restart_clvmd(int all_nodes);
|
|
||||||
int debug_clvmd(int level, int clusterwide);
|
|
||||||
|
|
||||||
1
daemons/cmirrord/.gitignore
vendored
1
daemons/cmirrord/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
cmirrord
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
CPG_LIBS = @CPG_LIBS@
|
|
||||||
CPG_CFLAGS = @CPG_CFLAGS@
|
|
||||||
SACKPT_LIBS = @SACKPT_LIBS@
|
|
||||||
SACKPT_CFLAGS = @SACKPT_CFLAGS@
|
|
||||||
|
|
||||||
SOURCES = clogd.c cluster.c compat.c functions.c link_mon.c local.c logging.c
|
|
||||||
|
|
||||||
TARGETS = cmirrord
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
LIBS += -ldevmapper
|
|
||||||
LMLIBS += $(CPG_LIBS) $(SACKPT_LIBS)
|
|
||||||
CFLAGS += $(CPG_CFLAGS) $(SACKPT_CFLAGS) $(EXTRA_EXEC_CFLAGS)
|
|
||||||
LDFLAGS += $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS)
|
|
||||||
|
|
||||||
cmirrord: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
|
|
||||||
$(LVMLIBS) $(LMLIBS) $(LIBS)
|
|
||||||
|
|
||||||
install: $(TARGETS)
|
|
||||||
$(INSTALL_PROGRAM) -D cmirrord $(usrsbindir)/cmirrord
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#include "logging.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "functions.h"
|
|
||||||
#include "link_mon.h"
|
|
||||||
#include "local.h"
|
|
||||||
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
static volatile sig_atomic_t exit_now = 0;
|
|
||||||
/* FIXME Review signal handling. Should be volatile sig_atomic_t */
|
|
||||||
static sigset_t signal_mask;
|
|
||||||
static volatile sig_atomic_t signal_received;
|
|
||||||
|
|
||||||
static void process_signals(void);
|
|
||||||
static void daemonize(void);
|
|
||||||
static void init_all(void);
|
|
||||||
static void cleanup_all(void);
|
|
||||||
|
|
||||||
static void usage (FILE *dest)
|
|
||||||
{
|
|
||||||
fprintf (dest, "Usage: cmirrord [options]\n"
|
|
||||||
" -f, --foreground stay in the foreground, log to the terminal\n"
|
|
||||||
" -h, --help print this help\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int foreground_mode = 0;
|
|
||||||
struct option longopts[] = {
|
|
||||||
{ "foreground", no_argument, NULL, 'f' },
|
|
||||||
{ "help" , no_argument, NULL, 'h' },
|
|
||||||
{ 0, 0, 0, 0 }
|
|
||||||
};
|
|
||||||
int opt;
|
|
||||||
|
|
||||||
while ((opt = getopt_long (argc, argv, "fh", longopts, NULL)) != -1) {
|
|
||||||
switch (opt) {
|
|
||||||
case 'f':
|
|
||||||
foreground_mode = 1;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
usage (stdout);
|
|
||||||
exit (0);
|
|
||||||
default:
|
|
||||||
usage (stderr);
|
|
||||||
exit (2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (optind < argc) {
|
|
||||||
usage (stderr);
|
|
||||||
exit (2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foreground_mode)
|
|
||||||
daemonize();
|
|
||||||
|
|
||||||
init_all();
|
|
||||||
|
|
||||||
/* Parent can now exit, we're ready to handle requests */
|
|
||||||
if (!foreground_mode)
|
|
||||||
kill(getppid(), SIGTERM);
|
|
||||||
|
|
||||||
LOG_PRINT("Starting cmirrord:");
|
|
||||||
LOG_PRINT(" Built: "__DATE__" "__TIME__"\n");
|
|
||||||
LOG_DBG(" Compiled with debugging.");
|
|
||||||
|
|
||||||
while (!exit_now) {
|
|
||||||
links_monitor();
|
|
||||||
|
|
||||||
links_issue_callbacks();
|
|
||||||
|
|
||||||
process_signals();
|
|
||||||
}
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* parent_exit_handler: exit the parent
|
|
||||||
* @sig: the signal
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void parent_exit_handler(int sig __attribute__((unused)))
|
|
||||||
{
|
|
||||||
exit_now = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sig_handler(int sig)
|
|
||||||
{
|
|
||||||
/* FIXME Races - don't touch signal_mask here. */
|
|
||||||
sigaddset(&signal_mask, sig);
|
|
||||||
signal_received = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void process_signal(int sig){
|
|
||||||
int r = 0;
|
|
||||||
|
|
||||||
switch(sig) {
|
|
||||||
case SIGINT:
|
|
||||||
case SIGQUIT:
|
|
||||||
case SIGTERM:
|
|
||||||
case SIGHUP:
|
|
||||||
r += log_status();
|
|
||||||
break;
|
|
||||||
case SIGUSR1:
|
|
||||||
case SIGUSR2:
|
|
||||||
log_debug();
|
|
||||||
/*local_debug();*/
|
|
||||||
cluster_debug();
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
LOG_PRINT("Unknown signal received... ignoring");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!r) {
|
|
||||||
LOG_DBG("No current cluster logs... safe to exit.");
|
|
||||||
cleanup_all();
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_ERROR("Cluster logs exist. Refusing to exit.");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void process_signals(void)
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
|
|
||||||
if (!signal_received)
|
|
||||||
return;
|
|
||||||
|
|
||||||
signal_received = 0;
|
|
||||||
|
|
||||||
for (x = 1; x < _NSIG; x++) {
|
|
||||||
if (sigismember(&signal_mask, x)) {
|
|
||||||
sigdelset(&signal_mask, x);
|
|
||||||
process_signal(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void remove_lockfile(void)
|
|
||||||
{
|
|
||||||
if (unlink(CMIRRORD_PIDFILE))
|
|
||||||
LOG_ERROR("Unable to remove \"" CMIRRORD_PIDFILE "\" %s", strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* daemonize
|
|
||||||
*
|
|
||||||
* Performs the steps necessary to become a daemon.
|
|
||||||
*/
|
|
||||||
static void daemonize(void)
|
|
||||||
{
|
|
||||||
int pid;
|
|
||||||
int status;
|
|
||||||
int devnull;
|
|
||||||
|
|
||||||
if ((devnull = open("/dev/null", O_RDWR)) == -1) {
|
|
||||||
LOG_ERROR("Can't open /dev/null: %s", strerror(errno));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
signal(SIGTERM, &parent_exit_handler);
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
|
|
||||||
if (pid < 0) {
|
|
||||||
LOG_ERROR("Unable to fork()");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid) {
|
|
||||||
/* Parent waits here for child to get going */
|
|
||||||
while (!waitpid(pid, &status, WNOHANG) && !exit_now);
|
|
||||||
if (exit_now)
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
|
|
||||||
switch (WEXITSTATUS(status)) {
|
|
||||||
case EXIT_LOCKFILE:
|
|
||||||
LOG_ERROR("Failed to create lockfile");
|
|
||||||
LOG_ERROR("Process already running?");
|
|
||||||
break;
|
|
||||||
case EXIT_KERNEL_SOCKET:
|
|
||||||
LOG_ERROR("Unable to create netlink socket");
|
|
||||||
break;
|
|
||||||
case EXIT_KERNEL_BIND:
|
|
||||||
LOG_ERROR("Unable to bind to netlink socket");
|
|
||||||
break;
|
|
||||||
case EXIT_KERNEL_SETSOCKOPT:
|
|
||||||
LOG_ERROR("Unable to setsockopt on netlink socket");
|
|
||||||
break;
|
|
||||||
case EXIT_CLUSTER_CKPT_INIT:
|
|
||||||
LOG_ERROR("Unable to initialize checkpoint service");
|
|
||||||
LOG_ERROR("Has the cluster infrastructure been started?");
|
|
||||||
break;
|
|
||||||
case EXIT_FAILURE:
|
|
||||||
LOG_ERROR("Failed to start: Generic error");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_ERROR("Failed to start: Unknown error");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
setsid();
|
|
||||||
if (chdir("/")) {
|
|
||||||
LOG_ERROR("Failed to chdir /: %s", strerror(errno));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
umask(0);
|
|
||||||
|
|
||||||
if (close(0) || close(1) || close(2)) {
|
|
||||||
LOG_ERROR("Failed to close terminal FDs");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((dup2(devnull, 0) < 0) || /* reopen stdin */
|
|
||||||
(dup2(devnull, 1) < 0) || /* reopen stdout */
|
|
||||||
(dup2(devnull, 2) < 0)) /* reopen stderr */
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
|
|
||||||
if ((devnull > STDERR_FILENO) && close(devnull)) {
|
|
||||||
LOG_ERROR("Failed to close descriptor %d: %s",
|
|
||||||
devnull, strerror(errno));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_OPEN("cmirrord", LOG_PID, LOG_DAEMON);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* init_all
|
|
||||||
*
|
|
||||||
* Initialize modules. Exit on failure.
|
|
||||||
*/
|
|
||||||
static void init_all(void)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
(void) dm_prepare_selinux_context(CMIRRORD_PIDFILE, S_IFREG);
|
|
||||||
if (dm_create_lockfile(CMIRRORD_PIDFILE) == 0)
|
|
||||||
exit(EXIT_LOCKFILE);
|
|
||||||
(void) dm_prepare_selinux_context(NULL, 0);
|
|
||||||
|
|
||||||
atexit(remove_lockfile);
|
|
||||||
|
|
||||||
/* FIXME Replace with sigaction. (deprecated) */
|
|
||||||
signal(SIGINT, &sig_handler);
|
|
||||||
signal(SIGQUIT, &sig_handler);
|
|
||||||
signal(SIGTERM, &sig_handler);
|
|
||||||
signal(SIGHUP, &sig_handler);
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
signal(SIGUSR1, &sig_handler);
|
|
||||||
signal(SIGUSR2, &sig_handler);
|
|
||||||
sigemptyset(&signal_mask);
|
|
||||||
signal_received = 0;
|
|
||||||
|
|
||||||
if ((r = init_local()) ||
|
|
||||||
(r = init_cluster())) {
|
|
||||||
exit(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* cleanup_all
|
|
||||||
*
|
|
||||||
* Clean up before exiting
|
|
||||||
*/
|
|
||||||
static void cleanup_all(void)
|
|
||||||
{
|
|
||||||
cleanup_local();
|
|
||||||
cleanup_cluster();
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#ifndef _LVM_CLOG_CLUSTER_H
|
|
||||||
#define _LVM_CLOG_CLUSTER_H
|
|
||||||
|
|
||||||
#include "dm-log-userspace.h"
|
|
||||||
#include "libdevmapper.h"
|
|
||||||
|
|
||||||
#define DM_ULOG_RESPONSE 0x1000U /* in last byte of 32-bit value */
|
|
||||||
#define DM_ULOG_CHECKPOINT_READY 21
|
|
||||||
#define DM_ULOG_MEMBER_JOIN 22
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There is other information in addition to what can
|
|
||||||
* be found in the dm_ulog_request structure that we
|
|
||||||
* need for processing. 'clog_request' is the wrapping
|
|
||||||
* structure we use to make the additional fields
|
|
||||||
* available.
|
|
||||||
*/
|
|
||||||
struct clog_request {
|
|
||||||
/*
|
|
||||||
* If we don't use a union, the structure size will
|
|
||||||
* vary between 32-bit and 64-bit machines. So, we
|
|
||||||
* pack two 64-bit version numbers in there to force
|
|
||||||
* the size of the structure to be the same.
|
|
||||||
*
|
|
||||||
* The two version numbers also help us with endian
|
|
||||||
* issues. The first is always little endian, while
|
|
||||||
* the second is in native format of the sending
|
|
||||||
* machine. If the two are equal, there is no need
|
|
||||||
* to do endian conversions.
|
|
||||||
*/
|
|
||||||
union {
|
|
||||||
uint64_t version[2]; /* LE version and native version */
|
|
||||||
struct dm_list list;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 'originator' is the machine from which the requests
|
|
||||||
* was made.
|
|
||||||
*/
|
|
||||||
uint32_t originator;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 'pit_server' is the "point-in-time" server for the
|
|
||||||
* request. (I.e. The machine that was the server at
|
|
||||||
* the time the request was issued - only important during
|
|
||||||
* startup.
|
|
||||||
*/
|
|
||||||
uint32_t pit_server;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The request from the kernel that is being processed
|
|
||||||
*/
|
|
||||||
struct dm_ulog_request u_rq;
|
|
||||||
};
|
|
||||||
|
|
||||||
int init_cluster(void);
|
|
||||||
void cleanup_cluster(void);
|
|
||||||
void cluster_debug(void);
|
|
||||||
|
|
||||||
int create_cluster_cpg(char *uuid, uint64_t luid);
|
|
||||||
int destroy_cluster_cpg(char *uuid);
|
|
||||||
|
|
||||||
int cluster_send(struct clog_request *rq);
|
|
||||||
|
|
||||||
#endif /* _LVM_CLOG_CLUSTER_H */
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#ifndef _LVM_CLOG_COMMON_H
|
|
||||||
#define _LVM_CLOG_COMMON_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there are problems when forking off to become a daemon,
|
|
||||||
* the child will exist with one of these codes. This allows
|
|
||||||
* the parent to know the reason for the failure and print it
|
|
||||||
* to the launching terminal.
|
|
||||||
*
|
|
||||||
* #define EXIT_SUCCESS 0 (from stdlib.h)
|
|
||||||
* #define EXIT_FAILURE 1 (from stdlib.h)
|
|
||||||
*/
|
|
||||||
#define EXIT_LOCKFILE 2
|
|
||||||
#define EXIT_KERNEL_SOCKET 3 /* Failed netlink socket create */
|
|
||||||
#define EXIT_KERNEL_BIND 4
|
|
||||||
#define EXIT_KERNEL_SETSOCKOPT 5
|
|
||||||
#define EXIT_CLUSTER_CKPT_INIT 6 /* Failed to init checkpoint */
|
|
||||||
#define EXIT_QUEUE_NOMEM 7
|
|
||||||
|
|
||||||
#define DM_ULOG_REQUEST_SIZE 1024
|
|
||||||
|
|
||||||
#endif /* _LVM_CLOG_COMMON_H */
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*/
|
|
||||||
#include "logging.h"
|
|
||||||
#include "cluster.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "xlate.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Older versions of the log daemon communicate with different
|
|
||||||
* versions of the inter-machine communication structure, which
|
|
||||||
* varies in size and fields. The older versions append the
|
|
||||||
* standard upstream version of the structure to every request.
|
|
||||||
* COMPAT_OFFSET is where the upstream structure starts.
|
|
||||||
*/
|
|
||||||
#define COMPAT_OFFSET 256
|
|
||||||
|
|
||||||
static void v5_data_endian_switch(struct clog_request *rq, int to_network __attribute__((unused)))
|
|
||||||
{
|
|
||||||
int i, end;
|
|
||||||
int64_t *pi64;
|
|
||||||
uint64_t *pu64;
|
|
||||||
uint32_t rq_type = rq->u_rq.request_type & ~DM_ULOG_RESPONSE;
|
|
||||||
|
|
||||||
if (rq->u_rq.request_type & DM_ULOG_RESPONSE) {
|
|
||||||
switch (rq_type) {
|
|
||||||
case DM_ULOG_CTR:
|
|
||||||
case DM_ULOG_DTR:
|
|
||||||
LOG_ERROR("Invalid response type in endian switch");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
|
|
||||||
case DM_ULOG_PRESUSPEND:
|
|
||||||
case DM_ULOG_POSTSUSPEND:
|
|
||||||
case DM_ULOG_RESUME:
|
|
||||||
case DM_ULOG_FLUSH:
|
|
||||||
case DM_ULOG_MARK_REGION:
|
|
||||||
case DM_ULOG_CLEAR_REGION:
|
|
||||||
case DM_ULOG_SET_REGION_SYNC:
|
|
||||||
case DM_ULOG_CHECKPOINT_READY:
|
|
||||||
case DM_ULOG_MEMBER_JOIN:
|
|
||||||
case DM_ULOG_STATUS_INFO:
|
|
||||||
case DM_ULOG_STATUS_TABLE:
|
|
||||||
/* No outbound data */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DM_ULOG_GET_REGION_SIZE:
|
|
||||||
case DM_ULOG_GET_SYNC_COUNT:
|
|
||||||
pu64 = (uint64_t *)rq->u_rq.data;
|
|
||||||
*pu64 = xlate64(*pu64);
|
|
||||||
break;
|
|
||||||
case DM_ULOG_IS_CLEAN:
|
|
||||||
case DM_ULOG_IN_SYNC:
|
|
||||||
pi64 = (int64_t *)rq->u_rq.data;
|
|
||||||
*pi64 = xlate64(*pi64);
|
|
||||||
break;
|
|
||||||
case DM_ULOG_GET_RESYNC_WORK:
|
|
||||||
case DM_ULOG_IS_REMOTE_RECOVERING:
|
|
||||||
pi64 = (int64_t *)rq->u_rq.data;
|
|
||||||
pu64 = ((uint64_t *)rq->u_rq.data) + 1;
|
|
||||||
*pi64 = xlate64(*pi64);
|
|
||||||
*pu64 = xlate64(*pu64);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_ERROR("Unknown request type, %u", rq_type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (rq_type) {
|
|
||||||
case DM_ULOG_CTR:
|
|
||||||
case DM_ULOG_DTR:
|
|
||||||
LOG_ERROR("Invalid request type in endian switch");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
|
|
||||||
case DM_ULOG_PRESUSPEND:
|
|
||||||
case DM_ULOG_POSTSUSPEND:
|
|
||||||
case DM_ULOG_RESUME:
|
|
||||||
case DM_ULOG_GET_REGION_SIZE:
|
|
||||||
case DM_ULOG_FLUSH:
|
|
||||||
case DM_ULOG_GET_RESYNC_WORK:
|
|
||||||
case DM_ULOG_GET_SYNC_COUNT:
|
|
||||||
case DM_ULOG_STATUS_INFO:
|
|
||||||
case DM_ULOG_STATUS_TABLE:
|
|
||||||
case DM_ULOG_CHECKPOINT_READY:
|
|
||||||
case DM_ULOG_MEMBER_JOIN:
|
|
||||||
/* No incoming data */
|
|
||||||
break;
|
|
||||||
case DM_ULOG_IS_CLEAN:
|
|
||||||
case DM_ULOG_IN_SYNC:
|
|
||||||
case DM_ULOG_IS_REMOTE_RECOVERING:
|
|
||||||
pu64 = (uint64_t *)rq->u_rq.data;
|
|
||||||
*pu64 = xlate64(*pu64);
|
|
||||||
break;
|
|
||||||
case DM_ULOG_MARK_REGION:
|
|
||||||
case DM_ULOG_CLEAR_REGION:
|
|
||||||
end = rq->u_rq.data_size/sizeof(uint64_t);
|
|
||||||
|
|
||||||
pu64 = (uint64_t *)rq->u_rq.data;
|
|
||||||
for (i = 0; i < end; i++)
|
|
||||||
pu64[i] = xlate64(pu64[i]);
|
|
||||||
break;
|
|
||||||
case DM_ULOG_SET_REGION_SYNC:
|
|
||||||
pu64 = (uint64_t *)rq->u_rq.data;
|
|
||||||
pi64 = ((int64_t *)rq->u_rq.data) + 1;
|
|
||||||
*pu64 = xlate64(*pu64);
|
|
||||||
*pi64 = xlate64(*pi64);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_ERROR("Unknown request type, %u", rq_type);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v5_endian_to_network(struct clog_request *rq)
|
|
||||||
{
|
|
||||||
int size;
|
|
||||||
struct dm_ulog_request *u_rq = &rq->u_rq;
|
|
||||||
|
|
||||||
size = sizeof(*rq) + u_rq->data_size;
|
|
||||||
|
|
||||||
u_rq->error = xlate32(u_rq->error);
|
|
||||||
u_rq->seq = xlate32(u_rq->seq);
|
|
||||||
|
|
||||||
rq->originator = xlate32(rq->originator);
|
|
||||||
|
|
||||||
v5_data_endian_switch(rq, 1);
|
|
||||||
|
|
||||||
u_rq->request_type = xlate32(u_rq->request_type);
|
|
||||||
u_rq->data_size = xlate32(u_rq->data_size);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int clog_request_to_network(struct clog_request *rq)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/* FIXME: Remove this safety check */
|
|
||||||
if (rq->u.version[0] != xlate64(rq->u.version[1])) {
|
|
||||||
LOG_ERROR("Programmer error: version[0] must be LE");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Are we already running in the endian mode we send
|
|
||||||
* over the wire?
|
|
||||||
*/
|
|
||||||
if (rq->u.version[0] == rq->u.version[1])
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
r = v5_endian_to_network(rq);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int v5_endian_from_network(struct clog_request *rq)
|
|
||||||
{
|
|
||||||
int size;
|
|
||||||
struct dm_ulog_request *u_rq = &rq->u_rq;
|
|
||||||
|
|
||||||
u_rq->error = xlate32(u_rq->error);
|
|
||||||
u_rq->seq = xlate32(u_rq->seq);
|
|
||||||
u_rq->request_type = xlate32(u_rq->request_type);
|
|
||||||
u_rq->data_size = xlate32(u_rq->data_size);
|
|
||||||
|
|
||||||
rq->originator = xlate32(rq->originator);
|
|
||||||
|
|
||||||
size = sizeof(*rq) + u_rq->data_size;
|
|
||||||
|
|
||||||
v5_data_endian_switch(rq, 0);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int clog_request_from_network(void *data, size_t data_len)
|
|
||||||
{
|
|
||||||
uint64_t *vp = data;
|
|
||||||
uint64_t version = xlate64(vp[0]);
|
|
||||||
struct clog_request *rq = data;
|
|
||||||
|
|
||||||
switch (version) {
|
|
||||||
case 5: /* Upstream */
|
|
||||||
if (version == vp[0])
|
|
||||||
return 0;
|
|
||||||
break;
|
|
||||||
case 4: /* RHEL 5.[45] */
|
|
||||||
case 3: /* RHEL 5.3 */
|
|
||||||
case 2: /* RHEL 5.2 */
|
|
||||||
/* FIXME: still need to account for payload */
|
|
||||||
if (data_len < (COMPAT_OFFSET + sizeof(*rq)))
|
|
||||||
return -ENOSPC;
|
|
||||||
|
|
||||||
rq = (struct clog_request *)((char *)data + COMPAT_OFFSET);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_ERROR("Unable to process cluster message: "
|
|
||||||
"Incompatible version");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
v5_endian_from_network(rq);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*/
|
|
||||||
#ifndef _LVM_CLOG_COMPAT_H
|
|
||||||
#define _LVM_CLOG_COMPAT_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The intermachine communication structure version are:
|
|
||||||
* 0: Unused
|
|
||||||
* 1: Never in the wild
|
|
||||||
* 2: RHEL 5.2
|
|
||||||
* 3: RHEL 5.3
|
|
||||||
* 4: RHEL 5.4, RHEL 5.5
|
|
||||||
* 5: RHEL 6, Current Upstream Format
|
|
||||||
*/
|
|
||||||
#define CLOG_TFR_VERSION 5
|
|
||||||
|
|
||||||
int clog_request_to_network(struct clog_request *rq);
|
|
||||||
int clog_request_from_network(void *data, size_t data_len);
|
|
||||||
|
|
||||||
#endif /* _LVM_CLOG_COMPAT_H */
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#ifndef _LVM_CLOG_FUNCTIONS_H
|
|
||||||
#define _LVM_CLOG_FUNCTIONS_H
|
|
||||||
|
|
||||||
#include "dm-log-userspace.h"
|
|
||||||
#include "cluster.h"
|
|
||||||
|
|
||||||
#define LOG_RESUMED 1
|
|
||||||
#define LOG_SUSPENDED 2
|
|
||||||
|
|
||||||
int local_resume(struct dm_ulog_request *rq);
|
|
||||||
int cluster_postsuspend(char *, uint64_t);
|
|
||||||
|
|
||||||
int do_request(struct clog_request *rq, int server);
|
|
||||||
int push_state(const char *uuid, uint64_t luid,
|
|
||||||
const char *which, char **buf, uint32_t debug_who);
|
|
||||||
int pull_state(const char *uuid, uint64_t luid,
|
|
||||||
const char *which, char *buf, int size);
|
|
||||||
|
|
||||||
int log_get_state(struct dm_ulog_request *rq);
|
|
||||||
int log_status(void);
|
|
||||||
void log_debug(void);
|
|
||||||
|
|
||||||
#endif /* _LVM_CLOG_FUNCTIONS_H */
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#include "logging.h"
|
|
||||||
#include "link_mon.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
struct link_callback {
|
|
||||||
int fd;
|
|
||||||
const char *name;
|
|
||||||
void *data;
|
|
||||||
int (*callback)(void *data);
|
|
||||||
|
|
||||||
struct link_callback *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned used_pfds = 0;
|
|
||||||
static unsigned free_pfds = 0;
|
|
||||||
static struct pollfd *pfds = NULL;
|
|
||||||
static struct link_callback *callbacks = NULL;
|
|
||||||
|
|
||||||
int links_register(int fd, const char *name, int (*callback)(void *data), void *data)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
struct link_callback *lc;
|
|
||||||
|
|
||||||
for (i = 0; i < used_pfds; i++) {
|
|
||||||
if (fd == pfds[i].fd) {
|
|
||||||
LOG_ERROR("links_register: Duplicate file descriptor");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lc = malloc(sizeof(*lc));
|
|
||||||
if (!lc)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
lc->fd = fd;
|
|
||||||
lc->name = name;
|
|
||||||
lc->data = data;
|
|
||||||
lc->callback = callback;
|
|
||||||
|
|
||||||
if (!free_pfds) {
|
|
||||||
struct pollfd *tmp;
|
|
||||||
tmp = realloc(pfds, sizeof(struct pollfd) * ((used_pfds*2) + 1));
|
|
||||||
if (!tmp) {
|
|
||||||
free(lc);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
pfds = tmp;
|
|
||||||
free_pfds = used_pfds + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free_pfds--;
|
|
||||||
pfds[used_pfds].fd = fd;
|
|
||||||
pfds[used_pfds].events = POLLIN;
|
|
||||||
pfds[used_pfds].revents = 0;
|
|
||||||
used_pfds++;
|
|
||||||
|
|
||||||
lc->next = callbacks;
|
|
||||||
callbacks = lc;
|
|
||||||
LOG_DBG("Adding %s/%d", lc->name, lc->fd);
|
|
||||||
LOG_DBG(" used_pfds = %u, free_pfds = %u",
|
|
||||||
used_pfds, free_pfds);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int links_unregister(int fd)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
struct link_callback *p, *c;
|
|
||||||
|
|
||||||
for (i = 0; i < used_pfds; i++)
|
|
||||||
if (fd == pfds[i].fd) {
|
|
||||||
/* entire struct is copied (overwritten) */
|
|
||||||
pfds[i] = pfds[used_pfds - 1];
|
|
||||||
used_pfds--;
|
|
||||||
free_pfds++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (p = NULL, c = callbacks; c; p = c, c = c->next)
|
|
||||||
if (fd == c->fd) {
|
|
||||||
LOG_DBG("Freeing up %s/%d", c->name, c->fd);
|
|
||||||
LOG_DBG(" used_pfds = %u, free_pfds = %u",
|
|
||||||
used_pfds, free_pfds);
|
|
||||||
if (p)
|
|
||||||
p->next = c->next;
|
|
||||||
else
|
|
||||||
callbacks = c->next;
|
|
||||||
free(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int links_monitor(void)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
for (i = 0; i < used_pfds; i++) {
|
|
||||||
pfds[i].revents = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = poll(pfds, used_pfds, -1);
|
|
||||||
if (r <= 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = 0;
|
|
||||||
/* FIXME: handle POLLHUP */
|
|
||||||
for (i = 0; i < used_pfds; i++)
|
|
||||||
if (pfds[i].revents & POLLIN) {
|
|
||||||
LOG_DBG("Data ready on %d", pfds[i].fd);
|
|
||||||
|
|
||||||
/* FIXME: Add this back return 1;*/
|
|
||||||
r++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int links_issue_callbacks(void)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
struct link_callback *lc;
|
|
||||||
|
|
||||||
for (i = 0; i < used_pfds; i++)
|
|
||||||
if (pfds[i].revents & POLLIN)
|
|
||||||
for (lc = callbacks; lc; lc = lc->next)
|
|
||||||
if (pfds[i].fd == lc->fd) {
|
|
||||||
LOG_DBG("Issuing callback on %s/%d",
|
|
||||||
lc->name, lc->fd);
|
|
||||||
lc->callback(lc->data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#ifndef _LVM_CLOG_LINK_MON_H
|
|
||||||
#define _LVM_CLOG_LINK_MON_H
|
|
||||||
|
|
||||||
int links_register(int fd, const char *name, int (*callback)(void *data), void *data);
|
|
||||||
int links_unregister(int fd);
|
|
||||||
int links_monitor(void);
|
|
||||||
int links_issue_callbacks(void);
|
|
||||||
|
|
||||||
#endif /* _LVM_CLOG_LINK_MON_H */
|
|
||||||
@@ -1,424 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#include "logging.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "functions.h"
|
|
||||||
#include "link_mon.h"
|
|
||||||
#include "local.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <linux/connector.h>
|
|
||||||
#include <linux/netlink.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#ifndef CN_IDX_DM
|
|
||||||
/* Kernel 2.6.31 is required to run this code */
|
|
||||||
#define CN_IDX_DM 0x7 /* Device Mapper */
|
|
||||||
#define CN_VAL_DM_USERSPACE_LOG 0x1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int cn_fd = -1; /* Connector (netlink) socket fd */
|
|
||||||
static char recv_buf[2048];
|
|
||||||
static char send_buf[2048];
|
|
||||||
|
|
||||||
|
|
||||||
/* FIXME: merge this function with kernel_send_helper */
|
|
||||||
static int kernel_ack(uint32_t seq, int error)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct nlmsghdr *nlh = (struct nlmsghdr *)send_buf;
|
|
||||||
struct cn_msg *msg = NLMSG_DATA(nlh);
|
|
||||||
|
|
||||||
if (error < 0) {
|
|
||||||
LOG_ERROR("Programmer error: error codes must be positive");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(send_buf, 0, sizeof(send_buf));
|
|
||||||
|
|
||||||
nlh->nlmsg_seq = 0;
|
|
||||||
nlh->nlmsg_pid = getpid();
|
|
||||||
nlh->nlmsg_type = NLMSG_DONE;
|
|
||||||
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct cn_msg));
|
|
||||||
nlh->nlmsg_flags = 0;
|
|
||||||
|
|
||||||
msg->len = 0;
|
|
||||||
msg->id.idx = CN_IDX_DM;
|
|
||||||
msg->id.val = CN_VAL_DM_USERSPACE_LOG;
|
|
||||||
msg->seq = seq;
|
|
||||||
msg->ack = error;
|
|
||||||
|
|
||||||
r = send(cn_fd, nlh, NLMSG_LENGTH(sizeof(struct cn_msg)), 0);
|
|
||||||
/* FIXME: do better error processing */
|
|
||||||
if (r <= 0)
|
|
||||||
return -EBADE;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* kernel_recv
|
|
||||||
* @rq: the newly allocated request from kernel
|
|
||||||
*
|
|
||||||
* Read requests from the kernel and allocate space for the new request.
|
|
||||||
* If there is no request from the kernel, *rq is NULL.
|
|
||||||
*
|
|
||||||
* This function is not thread safe due to returned stack pointer. In fact,
|
|
||||||
* the returned pointer must not be in-use when this function is called again.
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -EXXX on error
|
|
||||||
*/
|
|
||||||
static int kernel_recv(struct clog_request **rq)
|
|
||||||
{
|
|
||||||
int r = 0;
|
|
||||||
ssize_t len;
|
|
||||||
char *foo;
|
|
||||||
struct cn_msg *msg;
|
|
||||||
struct dm_ulog_request *u_rq;
|
|
||||||
struct nlmsghdr *nlmsg_h;
|
|
||||||
|
|
||||||
*rq = NULL;
|
|
||||||
memset(recv_buf, 0, sizeof(recv_buf));
|
|
||||||
|
|
||||||
len = recv(cn_fd, recv_buf, sizeof(recv_buf), 0);
|
|
||||||
if (len < 0) {
|
|
||||||
LOG_ERROR("Failed to recv message from kernel");
|
|
||||||
r = -errno;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
nlmsg_h = (struct nlmsghdr *)recv_buf;
|
|
||||||
switch (nlmsg_h->nlmsg_type) {
|
|
||||||
case NLMSG_ERROR:
|
|
||||||
LOG_ERROR("Unable to recv message from kernel: NLMSG_ERROR");
|
|
||||||
r = -EBADE;
|
|
||||||
goto fail;
|
|
||||||
case NLMSG_DONE:
|
|
||||||
msg = (struct cn_msg *)NLMSG_DATA((struct nlmsghdr *)recv_buf);
|
|
||||||
len -= (ssize_t)sizeof(struct nlmsghdr);
|
|
||||||
|
|
||||||
if (len < (ssize_t)sizeof(struct cn_msg)) {
|
|
||||||
LOG_ERROR("Incomplete request from kernel received");
|
|
||||||
r = -EBADE;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg->len > DM_ULOG_REQUEST_SIZE) {
|
|
||||||
LOG_ERROR("Not enough space to receive kernel request (%d/%d)",
|
|
||||||
msg->len, DM_ULOG_REQUEST_SIZE);
|
|
||||||
r = -EBADE;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!msg->len)
|
|
||||||
LOG_ERROR("Zero length message received");
|
|
||||||
|
|
||||||
len -= (ssize_t)sizeof(struct cn_msg);
|
|
||||||
|
|
||||||
if (len < msg->len)
|
|
||||||
LOG_ERROR("len = %zd, msg->len = %" PRIu16, len, msg->len);
|
|
||||||
|
|
||||||
msg->data[msg->len] = '\0'; /* Cleaner way to ensure this? */
|
|
||||||
u_rq = (struct dm_ulog_request *)msg->data;
|
|
||||||
|
|
||||||
if (!u_rq->request_type) {
|
|
||||||
LOG_DBG("Bad transmission, requesting resend [%u]",
|
|
||||||
msg->seq);
|
|
||||||
r = -EAGAIN;
|
|
||||||
|
|
||||||
if (kernel_ack(msg->seq, EAGAIN)) {
|
|
||||||
LOG_ERROR("Failed to NACK kernel transmission [%u]",
|
|
||||||
msg->seq);
|
|
||||||
r = -EBADE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now we've got sizeof(struct cn_msg) + sizeof(struct nlmsghdr)
|
|
||||||
* worth of space that precede the request structure from the
|
|
||||||
* kernel. Since that space isn't going to be used again, we
|
|
||||||
* can take it for our purposes; rather than allocating a whole
|
|
||||||
* new structure and doing a memcpy.
|
|
||||||
*
|
|
||||||
* We should really make sure 'clog_request' doesn't grow
|
|
||||||
* beyond what is available to us, but we need only check it
|
|
||||||
* once... perhaps at compile time?
|
|
||||||
*/
|
|
||||||
foo = (char *)u_rq;
|
|
||||||
foo -= (sizeof(struct clog_request) - sizeof(struct dm_ulog_request));
|
|
||||||
*rq = (struct clog_request *) foo;
|
|
||||||
|
|
||||||
/* Clear the wrapper container fields */
|
|
||||||
memset(*rq, 0, (size_t)((char *)u_rq - (char *)(*rq)));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_ERROR("Unknown nlmsg_type");
|
|
||||||
r = -EBADE;
|
|
||||||
}
|
|
||||||
|
|
||||||
fail:
|
|
||||||
if (r)
|
|
||||||
*rq = NULL;
|
|
||||||
|
|
||||||
return (r == -EAGAIN) ? 0 : r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int kernel_send_helper(void *data, uint16_t out_size)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct nlmsghdr *nlh;
|
|
||||||
struct cn_msg *msg;
|
|
||||||
|
|
||||||
memset(send_buf, 0, sizeof(send_buf));
|
|
||||||
|
|
||||||
nlh = (struct nlmsghdr *)send_buf;
|
|
||||||
nlh->nlmsg_seq = 0; /* FIXME: Is this used? */
|
|
||||||
nlh->nlmsg_pid = getpid();
|
|
||||||
nlh->nlmsg_type = NLMSG_DONE;
|
|
||||||
nlh->nlmsg_len = NLMSG_LENGTH(out_size + sizeof(struct cn_msg));
|
|
||||||
nlh->nlmsg_flags = 0;
|
|
||||||
|
|
||||||
msg = NLMSG_DATA(nlh);
|
|
||||||
memcpy(msg->data, data, out_size);
|
|
||||||
msg->len = out_size;
|
|
||||||
msg->id.idx = CN_IDX_DM;
|
|
||||||
msg->id.val = CN_VAL_DM_USERSPACE_LOG;
|
|
||||||
msg->seq = 0;
|
|
||||||
|
|
||||||
r = send(cn_fd, nlh, NLMSG_LENGTH(out_size + sizeof(struct cn_msg)), 0);
|
|
||||||
/* FIXME: do better error processing */
|
|
||||||
if (r <= 0)
|
|
||||||
return -EBADE;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* do_local_work
|
|
||||||
*
|
|
||||||
* Any processing errors are placed in the 'rq'
|
|
||||||
* structure to be reported back to the kernel.
|
|
||||||
* It may be pointless for this function to
|
|
||||||
* return an int.
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -EXXX on failure
|
|
||||||
*/
|
|
||||||
static int do_local_work(void *data __attribute__((unused)))
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct clog_request *rq;
|
|
||||||
struct dm_ulog_request *u_rq = NULL;
|
|
||||||
|
|
||||||
r = kernel_recv(&rq);
|
|
||||||
if (r)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (!rq)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
u_rq = &rq->u_rq;
|
|
||||||
LOG_DBG("[%s] Request from kernel received: [%s/%u]",
|
|
||||||
SHORT_UUID(u_rq->uuid), RQ_TYPE(u_rq->request_type),
|
|
||||||
u_rq->seq);
|
|
||||||
switch (u_rq->request_type) {
|
|
||||||
case DM_ULOG_CTR:
|
|
||||||
case DM_ULOG_DTR:
|
|
||||||
case DM_ULOG_GET_REGION_SIZE:
|
|
||||||
case DM_ULOG_IN_SYNC:
|
|
||||||
case DM_ULOG_GET_SYNC_COUNT:
|
|
||||||
case DM_ULOG_STATUS_TABLE:
|
|
||||||
case DM_ULOG_PRESUSPEND:
|
|
||||||
/* We do not specify ourselves as server here */
|
|
||||||
r = do_request(rq, 0);
|
|
||||||
if (r)
|
|
||||||
LOG_DBG("Returning failed request to kernel [%s]",
|
|
||||||
RQ_TYPE(u_rq->request_type));
|
|
||||||
r = kernel_send(u_rq);
|
|
||||||
if (r)
|
|
||||||
LOG_ERROR("Failed to respond to kernel [%s]",
|
|
||||||
RQ_TYPE(u_rq->request_type));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case DM_ULOG_RESUME:
|
|
||||||
/*
|
|
||||||
* Resume is a special case that requires a local
|
|
||||||
* component to join the CPG, and a cluster component
|
|
||||||
* to handle the request.
|
|
||||||
*/
|
|
||||||
r = local_resume(u_rq);
|
|
||||||
if (r) {
|
|
||||||
LOG_DBG("Returning failed request to kernel [%s]",
|
|
||||||
RQ_TYPE(u_rq->request_type));
|
|
||||||
r = kernel_send(u_rq);
|
|
||||||
if (r)
|
|
||||||
LOG_ERROR("Failed to respond to kernel [%s]",
|
|
||||||
RQ_TYPE(u_rq->request_type));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* ELSE, fall through */
|
|
||||||
case DM_ULOG_IS_CLEAN:
|
|
||||||
case DM_ULOG_FLUSH:
|
|
||||||
case DM_ULOG_MARK_REGION:
|
|
||||||
case DM_ULOG_GET_RESYNC_WORK:
|
|
||||||
case DM_ULOG_SET_REGION_SYNC:
|
|
||||||
case DM_ULOG_STATUS_INFO:
|
|
||||||
case DM_ULOG_IS_REMOTE_RECOVERING:
|
|
||||||
case DM_ULOG_POSTSUSPEND:
|
|
||||||
r = cluster_send(rq);
|
|
||||||
if (r) {
|
|
||||||
u_rq->data_size = 0;
|
|
||||||
u_rq->error = r;
|
|
||||||
if (kernel_send(u_rq))
|
|
||||||
LOG_ERROR("Failed to respond to kernel [%s]",
|
|
||||||
RQ_TYPE(u_rq->request_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case DM_ULOG_CLEAR_REGION:
|
|
||||||
r = kernel_ack(u_rq->seq, 0);
|
|
||||||
|
|
||||||
r = cluster_send(rq);
|
|
||||||
if (r) {
|
|
||||||
/*
|
|
||||||
* FIXME: store error for delivery on flush
|
|
||||||
* This would allow us to optimize MARK_REGION
|
|
||||||
* too.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_ERROR("Invalid log request received (%u), ignoring.",
|
|
||||||
u_rq->request_type);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r && !u_rq->error)
|
|
||||||
u_rq->error = r;
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* kernel_send
|
|
||||||
* @u_rq: result to pass back to kernel
|
|
||||||
*
|
|
||||||
* This function returns the u_rq structure
|
|
||||||
* (containing the results) to the kernel.
|
|
||||||
* It then frees the structure.
|
|
||||||
*
|
|
||||||
* WARNING: should the structure be freed if
|
|
||||||
* there is an error? I vote 'yes'. If the
|
|
||||||
* kernel doesn't get the response, it should
|
|
||||||
* resend the request.
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, -EXXX on failure
|
|
||||||
*/
|
|
||||||
int kernel_send(struct dm_ulog_request *u_rq)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
uint16_t size;
|
|
||||||
|
|
||||||
if (!u_rq)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
size = (uint16_t)(sizeof(struct dm_ulog_request) + u_rq->data_size);
|
|
||||||
|
|
||||||
if (!u_rq->data_size && !u_rq->error) {
|
|
||||||
/* An ACK is all that is needed */
|
|
||||||
|
|
||||||
/* FIXME: add ACK code */
|
|
||||||
} else if (size > DM_ULOG_REQUEST_SIZE) {
|
|
||||||
/*
|
|
||||||
* If we gotten here, we've already overrun
|
|
||||||
* our allotted space somewhere.
|
|
||||||
*
|
|
||||||
* We must do something, because the kernel
|
|
||||||
* is waiting for a response.
|
|
||||||
*/
|
|
||||||
LOG_ERROR("Not enough space to respond to server");
|
|
||||||
u_rq->error = -ENOSPC;
|
|
||||||
size = sizeof(struct dm_ulog_request);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = kernel_send_helper(u_rq, size);
|
|
||||||
if (r)
|
|
||||||
LOG_ERROR("Failed to send msg to kernel.");
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* init_local
|
|
||||||
*
|
|
||||||
* Initialize kernel communication socket (netlink)
|
|
||||||
*
|
|
||||||
* Returns: 0 on success, values from common.h on failure
|
|
||||||
*/
|
|
||||||
int init_local(void)
|
|
||||||
{
|
|
||||||
int r = 0;
|
|
||||||
unsigned opt;
|
|
||||||
struct sockaddr_nl addr;
|
|
||||||
|
|
||||||
cn_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
|
|
||||||
if (cn_fd < 0)
|
|
||||||
return EXIT_KERNEL_SOCKET;
|
|
||||||
|
|
||||||
/* memset to fix valgrind complaint */
|
|
||||||
memset(&addr, 0, sizeof(struct sockaddr_nl));
|
|
||||||
|
|
||||||
addr.nl_family = AF_NETLINK;
|
|
||||||
addr.nl_groups = CN_IDX_DM;
|
|
||||||
addr.nl_pid = 0;
|
|
||||||
|
|
||||||
r = bind(cn_fd, (struct sockaddr *) &addr, sizeof(addr));
|
|
||||||
if (r < 0) {
|
|
||||||
if (close(cn_fd))
|
|
||||||
LOG_ERROR("Failed to close socket: %s",
|
|
||||||
strerror(errno));
|
|
||||||
return EXIT_KERNEL_BIND;
|
|
||||||
}
|
|
||||||
|
|
||||||
opt = addr.nl_groups;
|
|
||||||
r = setsockopt(cn_fd, 270, NETLINK_ADD_MEMBERSHIP, &opt, sizeof(opt));
|
|
||||||
if (r) {
|
|
||||||
if (close(cn_fd))
|
|
||||||
LOG_ERROR("Failed to close socket: %s",
|
|
||||||
strerror(errno));
|
|
||||||
return EXIT_KERNEL_SETSOCKOPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
r = fcntl(cn_fd, F_SETFL, FNDELAY);
|
|
||||||
*/
|
|
||||||
|
|
||||||
links_register(cn_fd, "local", do_local_work, NULL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* cleanup_local
|
|
||||||
*
|
|
||||||
* Clean up before exiting
|
|
||||||
*/
|
|
||||||
void cleanup_local(void)
|
|
||||||
{
|
|
||||||
links_unregister(cn_fd);
|
|
||||||
if (cn_fd >= 0 && close(cn_fd))
|
|
||||||
LOG_ERROR("Failed to close socket: %s",
|
|
||||||
strerror(errno));
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#ifndef _LVM_CLOG_LOCAL_H
|
|
||||||
#define _LVM_CLOG_LOCAL_H
|
|
||||||
|
|
||||||
int init_local(void);
|
|
||||||
void cleanup_local(void);
|
|
||||||
|
|
||||||
int kernel_send(struct dm_ulog_request *rq);
|
|
||||||
|
|
||||||
#endif /* _LVM_CLOG_LOCAL_H */
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
#include "logging.h"
|
|
||||||
|
|
||||||
const char *__rq_types_off_by_one[] = {
|
|
||||||
"DM_ULOG_CTR",
|
|
||||||
"DM_ULOG_DTR",
|
|
||||||
"DM_ULOG_PRESUSPEND",
|
|
||||||
"DM_ULOG_POSTSUSPEND",
|
|
||||||
"DM_ULOG_RESUME",
|
|
||||||
"DM_ULOG_GET_REGION_SIZE",
|
|
||||||
"DM_ULOG_IS_CLEAN",
|
|
||||||
"DM_ULOG_IN_SYNC",
|
|
||||||
"DM_ULOG_FLUSH",
|
|
||||||
"DM_ULOG_MARK_REGION",
|
|
||||||
"DM_ULOG_CLEAR_REGION",
|
|
||||||
"DM_ULOG_GET_RESYNC_WORK",
|
|
||||||
"DM_ULOG_SET_REGION_SYNC",
|
|
||||||
"DM_ULOG_GET_SYNC_COUNT",
|
|
||||||
"DM_ULOG_STATUS_INFO",
|
|
||||||
"DM_ULOG_STATUS_TABLE",
|
|
||||||
"DM_ULOG_IS_REMOTE_RECOVERING",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
int log_tabbing = 0;
|
|
||||||
int log_is_open = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Variables for various conditional logging
|
|
||||||
*/
|
|
||||||
#ifdef MEMB
|
|
||||||
int log_membership_change = 1;
|
|
||||||
#else
|
|
||||||
int log_membership_change = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CKPT
|
|
||||||
int log_checkpoint = 1;
|
|
||||||
#else
|
|
||||||
int log_checkpoint = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef RESEND
|
|
||||||
int log_resend_requests = 1;
|
|
||||||
#else
|
|
||||||
int log_resend_requests = 0;
|
|
||||||
#endif
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _LVM_CLOG_LOGGING_H
|
|
||||||
#define _LVM_CLOG_LOGGING_H
|
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#define _FILE_OFFSET_BITS 64
|
|
||||||
|
|
||||||
#include "configure.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
/* SHORT_UUID - print last 8 chars of a string */
|
|
||||||
#define SHORT_UUID(x) (strlen(x) > 8) ? ((x) + (strlen(x) - 8)) : (x)
|
|
||||||
|
|
||||||
extern const char *__rq_types_off_by_one[];
|
|
||||||
#define RQ_TYPE(x) __rq_types_off_by_one[(x) - 1]
|
|
||||||
|
|
||||||
extern int log_tabbing;
|
|
||||||
extern int log_is_open;
|
|
||||||
extern int log_membership_change;
|
|
||||||
extern int log_checkpoint;
|
|
||||||
extern int log_resend_requests;
|
|
||||||
|
|
||||||
#define LOG_OPEN(ident, option, facility) do { \
|
|
||||||
openlog(ident, option, facility); \
|
|
||||||
log_is_open = 1; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LOG_CLOSE(void) do { \
|
|
||||||
log_is_open = 0; \
|
|
||||||
closelog(); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LOG_OUTPUT(level, f, arg...) do { \
|
|
||||||
int __i; \
|
|
||||||
char __buffer[16]; \
|
|
||||||
FILE *fp = (level > LOG_NOTICE) ? stderr : stdout; \
|
|
||||||
if (log_is_open) { \
|
|
||||||
for (__i = 0; (__i < log_tabbing) && (__i < 15); __i++) \
|
|
||||||
__buffer[__i] = '\t'; \
|
|
||||||
__buffer[__i] = '\0'; \
|
|
||||||
syslog(level, "%s" f "\n", __buffer, ## arg); \
|
|
||||||
} else { \
|
|
||||||
for (__i = 0; __i < log_tabbing; __i++) \
|
|
||||||
fprintf(fp, "\t"); \
|
|
||||||
fprintf(fp, f "\n", ## arg); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
#define LOG_DBG(f, arg...) LOG_OUTPUT(LOG_DEBUG, f, ## arg)
|
|
||||||
#else /* DEBUG */
|
|
||||||
#define LOG_DBG(f, arg...) do {} while (0)
|
|
||||||
#endif /* DEBUG */
|
|
||||||
|
|
||||||
#define LOG_COND(__X, f, arg...) do {\
|
|
||||||
if (__X) { \
|
|
||||||
LOG_OUTPUT(LOG_NOTICE, f, ## arg); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
#define LOG_PRINT(f, arg...) LOG_OUTPUT(LOG_NOTICE, f, ## arg)
|
|
||||||
#define LOG_ERROR(f, arg...) LOG_OUTPUT(LOG_ERR, f, ## arg)
|
|
||||||
|
|
||||||
#endif /* _LVM_CLOG_LOGGING_H */
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
init_fifos
|
|
||||||
fini_fifos
|
|
||||||
daemon_talk
|
|
||||||
dm_event_get_version
|
|
||||||
1
daemons/dmeventd/.gitignore
vendored
1
daemons/dmeventd/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
dmeventd
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2005-2011 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of the device-mapper userspace tools.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU Lesser General Public License v.2.1.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
SOURCES = libdevmapper-event.c
|
|
||||||
SOURCES2 = dmeventd.c
|
|
||||||
|
|
||||||
TARGETS = dmeventd
|
|
||||||
|
|
||||||
.PHONY: install_lib_dynamic install_lib_static install_include \
|
|
||||||
install_pkgconfig install_dmeventd_dynamic install_dmeventd_static \
|
|
||||||
install_lib install_dmeventd
|
|
||||||
|
|
||||||
INSTALL_DMEVENTD_TARGETS = install_dmeventd_dynamic
|
|
||||||
INSTALL_LIB_TARGETS = install_lib_dynamic
|
|
||||||
|
|
||||||
LIB_NAME = libdevmapper-event
|
|
||||||
ifeq ("@STATIC_LINK@", "yes")
|
|
||||||
LIB_STATIC = $(LIB_NAME).a
|
|
||||||
TARGETS += $(LIB_STATIC) dmeventd.static
|
|
||||||
INSTALL_DMEVENTD_TARGETS += install_dmeventd_static
|
|
||||||
INSTALL_LIB_TARGETS += install_lib_static
|
|
||||||
endif
|
|
||||||
|
|
||||||
LIB_VERSION = $(LIB_VERSION_DM)
|
|
||||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
|
||||||
|
|
||||||
CLEAN_TARGETS = dmeventd.static $(LIB_NAME).a
|
|
||||||
|
|
||||||
ifneq ($(MAKECMDGOALS),device-mapper)
|
|
||||||
SUBDIRS+=plugins
|
|
||||||
endif
|
|
||||||
|
|
||||||
CFLOW_LIST = $(SOURCES)
|
|
||||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
|
||||||
CFLOW_TARGET = dmeventd
|
|
||||||
|
|
||||||
EXPORTED_HEADER = $(srcdir)/libdevmapper-event.h
|
|
||||||
EXPORTED_FN_PREFIX = dm_event
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
all: device-mapper
|
|
||||||
device-mapper: $(TARGETS)
|
|
||||||
|
|
||||||
CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS)
|
|
||||||
LIBS += -ldevmapper $(PTHREAD_LIBS)
|
|
||||||
|
|
||||||
dmeventd: $(LIB_SHARED) dmeventd.o
|
|
||||||
$(CC) $(CFLAGS) -L. $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) dmeventd.o \
|
|
||||||
-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS)
|
|
||||||
|
|
||||||
dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) dmeventd.o \
|
|
||||||
-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS) $(STATIC_LIBS)
|
|
||||||
|
|
||||||
ifeq ("@PKGCONFIG@", "yes")
|
|
||||||
INSTALL_LIB_TARGETS += install_pkgconfig
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ("$(CFLOW_CMD)", "")
|
|
||||||
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
|
|
||||||
-include $(top_builddir)/libdm/libdevmapper.cflow
|
|
||||||
-include $(top_builddir)/lib/liblvm-internal.cflow
|
|
||||||
-include $(top_builddir)/lib/liblvm2cmd.cflow
|
|
||||||
-include $(top_builddir)/daemons/dmeventd/$(LIB_NAME).cflow
|
|
||||||
-include $(top_builddir)/daemons/dmeventd/plugins/mirror/$(LIB_NAME)-lvm2mirror.cflow
|
|
||||||
endif
|
|
||||||
|
|
||||||
install_include: $(srcdir)/libdevmapper-event.h
|
|
||||||
$(INSTALL_DATA) -D $< $(includedir)/$(<F)
|
|
||||||
|
|
||||||
install_pkgconfig: libdevmapper-event.pc
|
|
||||||
$(INSTALL_DATA) -D $< $(pkgconfigdir)/devmapper-event.pc
|
|
||||||
|
|
||||||
install_lib_dynamic: install_lib_shared
|
|
||||||
|
|
||||||
install_lib_static: $(LIB_STATIC)
|
|
||||||
$(INSTALL_DATA) -D $< $(usrlibdir)/$(<F)
|
|
||||||
|
|
||||||
install_lib: $(INSTALL_LIB_TARGETS)
|
|
||||||
|
|
||||||
install_dmeventd_dynamic: dmeventd
|
|
||||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
|
||||||
|
|
||||||
install_dmeventd_static: dmeventd.static
|
|
||||||
$(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
|
|
||||||
|
|
||||||
install_dmeventd: $(INSTALL_DMEVENTD_TARGETS)
|
|
||||||
|
|
||||||
install: install_include install_lib install_dmeventd
|
|
||||||
|
|
||||||
install_device-mapper: install_include install_lib install_dmeventd
|
|
||||||
|
|
||||||
DISTCLEAN_TARGETS += libdevmapper-event.pc
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of the device-mapper userspace tools.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __DMEVENTD_DOT_H__
|
|
||||||
#define __DMEVENTD_DOT_H__
|
|
||||||
|
|
||||||
/* FIXME This stuff must be configurable. */
|
|
||||||
|
|
||||||
#define DM_EVENT_FIFO_CLIENT DEFAULT_DM_RUN_DIR "/dmeventd-client"
|
|
||||||
#define DM_EVENT_FIFO_SERVER DEFAULT_DM_RUN_DIR "/dmeventd-server"
|
|
||||||
|
|
||||||
#define DM_EVENT_DEFAULT_TIMEOUT 10
|
|
||||||
|
|
||||||
/* Commands for the daemon passed in the message below. */
|
|
||||||
enum dm_event_command {
|
|
||||||
DM_EVENT_CMD_ACTIVE = 1,
|
|
||||||
DM_EVENT_CMD_REGISTER_FOR_EVENT,
|
|
||||||
DM_EVENT_CMD_UNREGISTER_FOR_EVENT,
|
|
||||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE,
|
|
||||||
DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
|
|
||||||
DM_EVENT_CMD_SET_TIMEOUT,
|
|
||||||
DM_EVENT_CMD_GET_TIMEOUT,
|
|
||||||
DM_EVENT_CMD_HELLO,
|
|
||||||
DM_EVENT_CMD_DIE,
|
|
||||||
DM_EVENT_CMD_GET_STATUS,
|
|
||||||
DM_EVENT_CMD_GET_PARAMETERS,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Message passed between client and daemon. */
|
|
||||||
struct dm_event_daemon_message {
|
|
||||||
uint32_t cmd;
|
|
||||||
uint32_t size;
|
|
||||||
char *data;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* FIXME Is this meant to be exported? I can't see where the
|
|
||||||
interface uses it. */
|
|
||||||
/* Fifos for client/daemon communication. */
|
|
||||||
struct dm_event_fifos {
|
|
||||||
int client;
|
|
||||||
int server;
|
|
||||||
const char *client_path;
|
|
||||||
const char *server_path;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* EXIT_SUCCESS 0 -- stdlib.h */
|
|
||||||
/* EXIT_FAILURE 1 -- stdlib.h */
|
|
||||||
/* EXIT_LOCKFILE_INUSE 2 -- obsoleted */
|
|
||||||
#define EXIT_DESC_CLOSE_FAILURE 3
|
|
||||||
#define EXIT_DESC_OPEN_FAILURE 4
|
|
||||||
/* EXIT_OPEN_PID_FAILURE 5 -- obsoleted */
|
|
||||||
#define EXIT_FIFO_FAILURE 6
|
|
||||||
#define EXIT_CHDIR_FAILURE 7
|
|
||||||
|
|
||||||
/* Implemented in libdevmapper-event.c, but not part of public API. */
|
|
||||||
// FIXME misuse of bitmask as enum
|
|
||||||
int daemon_talk(struct dm_event_fifos *fifos,
|
|
||||||
struct dm_event_daemon_message *msg, int cmd,
|
|
||||||
const char *dso_name, const char *dev_name,
|
|
||||||
enum dm_event_mask evmask, uint32_t timeout);
|
|
||||||
int init_fifos(struct dm_event_fifos *fifos);
|
|
||||||
void fini_fifos(struct dm_event_fifos *fifos);
|
|
||||||
int dm_event_get_version(struct dm_event_fifos *fifos, int *version);
|
|
||||||
|
|
||||||
#endif /* __DMEVENTD_DOT_H__ */
|
|
||||||
@@ -1,994 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of the device-mapper userspace tools.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dm-logging.h"
|
|
||||||
#include "dmlib.h"
|
|
||||||
#include "libdevmapper-event.h"
|
|
||||||
#include "dmeventd.h"
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/file.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <arpa/inet.h> /* for htonl, ntohl */
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
static int _debug_level = 0;
|
|
||||||
static int _use_syslog = 0;
|
|
||||||
static int _sequence_nr = 0;
|
|
||||||
|
|
||||||
struct dm_event_handler {
|
|
||||||
char *dso;
|
|
||||||
|
|
||||||
char *dmeventd_path;
|
|
||||||
|
|
||||||
char *dev_name;
|
|
||||||
|
|
||||||
char *uuid;
|
|
||||||
int major;
|
|
||||||
int minor;
|
|
||||||
uint32_t timeout;
|
|
||||||
|
|
||||||
enum dm_event_mask mask;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void _dm_event_handler_clear_dev_info(struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
dm_free(dmevh->dev_name);
|
|
||||||
dm_free(dmevh->uuid);
|
|
||||||
dmevh->dev_name = dmevh->uuid = NULL;
|
|
||||||
dmevh->major = dmevh->minor = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dm_event_handler *dm_event_handler_create(void)
|
|
||||||
{
|
|
||||||
struct dm_event_handler *dmevh;
|
|
||||||
|
|
||||||
if (!(dmevh = dm_zalloc(sizeof(*dmevh)))) {
|
|
||||||
log_error("Failed to allocate event handler.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dmevh;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dm_event_handler_destroy(struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
_dm_event_handler_clear_dev_info(dmevh);
|
|
||||||
dm_free(dmevh->dso);
|
|
||||||
dm_free(dmevh->dmeventd_path);
|
|
||||||
dm_free(dmevh);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_handler_set_dmeventd_path(struct dm_event_handler *dmevh, const char *dmeventd_path)
|
|
||||||
{
|
|
||||||
if (!dmeventd_path) /* noop */
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
dm_free(dmevh->dmeventd_path);
|
|
||||||
|
|
||||||
if (!(dmevh->dmeventd_path = dm_strdup(dmeventd_path)))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path)
|
|
||||||
{
|
|
||||||
if (!path) /* noop */
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
dm_free(dmevh->dso);
|
|
||||||
|
|
||||||
if (!(dmevh->dso = dm_strdup(path)))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *dev_name)
|
|
||||||
{
|
|
||||||
if (!dev_name)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
_dm_event_handler_clear_dev_info(dmevh);
|
|
||||||
|
|
||||||
if (!(dmevh->dev_name = dm_strdup(dev_name)))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid)
|
|
||||||
{
|
|
||||||
if (!uuid)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
_dm_event_handler_clear_dev_info(dmevh);
|
|
||||||
|
|
||||||
if (!(dmevh->uuid = dm_strdup(uuid)))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major)
|
|
||||||
{
|
|
||||||
int minor = dmevh->minor;
|
|
||||||
|
|
||||||
_dm_event_handler_clear_dev_info(dmevh);
|
|
||||||
|
|
||||||
dmevh->major = major;
|
|
||||||
dmevh->minor = minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor)
|
|
||||||
{
|
|
||||||
int major = dmevh->major;
|
|
||||||
|
|
||||||
_dm_event_handler_clear_dev_info(dmevh);
|
|
||||||
|
|
||||||
dmevh->major = major;
|
|
||||||
dmevh->minor = minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
|
|
||||||
enum dm_event_mask evmask)
|
|
||||||
{
|
|
||||||
dmevh->mask = evmask;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout)
|
|
||||||
{
|
|
||||||
dmevh->timeout = timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
return dmevh->dso;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
return dmevh->dev_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
return dmevh->uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_handler_get_major(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
return dmevh->major;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_handler_get_minor(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
return dmevh->minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
return dmevh->timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
return dmevh->mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _check_message_id(struct dm_event_daemon_message *msg)
|
|
||||||
{
|
|
||||||
int pid, seq_nr;
|
|
||||||
|
|
||||||
if ((sscanf(msg->data, "%d:%d", &pid, &seq_nr) != 2) ||
|
|
||||||
(pid != getpid()) || (seq_nr != _sequence_nr)) {
|
|
||||||
log_error("Ignoring out-of-sequence reply from dmeventd. "
|
|
||||||
"Expected %d:%d but received %s.", getpid(),
|
|
||||||
_sequence_nr, msg->data);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* daemon_read
|
|
||||||
* @fifos
|
|
||||||
* @msg
|
|
||||||
*
|
|
||||||
* Read message from daemon.
|
|
||||||
*
|
|
||||||
* Returns: 0 on failure, 1 on success
|
|
||||||
*/
|
|
||||||
static int _daemon_read(struct dm_event_fifos *fifos,
|
|
||||||
struct dm_event_daemon_message *msg)
|
|
||||||
{
|
|
||||||
unsigned bytes = 0;
|
|
||||||
int ret, i;
|
|
||||||
fd_set fds;
|
|
||||||
size_t size = 2 * sizeof(uint32_t); /* status + size */
|
|
||||||
uint32_t *header = alloca(size);
|
|
||||||
char *buf = (char *)header;
|
|
||||||
|
|
||||||
while (bytes < size) {
|
|
||||||
for (i = 0, ret = 0; (i < 20) && (ret < 1); i++) {
|
|
||||||
/* Watch daemon read FIFO for input. */
|
|
||||||
struct timeval tval = { .tv_sec = 1 };
|
|
||||||
FD_ZERO(&fds);
|
|
||||||
FD_SET(fifos->server, &fds);
|
|
||||||
ret = select(fifos->server + 1, &fds, NULL, NULL, &tval);
|
|
||||||
if (ret < 0 && errno != EINTR) {
|
|
||||||
log_error("Unable to read from event server.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if ((ret == 0) && (i > 4) && !bytes) {
|
|
||||||
log_error("No input from event server.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ret < 1) {
|
|
||||||
log_error("Unable to read from event server.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = read(fifos->server, buf + bytes, size);
|
|
||||||
if (ret < 0) {
|
|
||||||
if ((errno == EINTR) || (errno == EAGAIN))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
log_error("Unable to read from event server.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes += ret;
|
|
||||||
if (header && (bytes == 2 * sizeof(uint32_t))) {
|
|
||||||
msg->cmd = ntohl(header[0]);
|
|
||||||
msg->size = ntohl(header[1]);
|
|
||||||
buf = msg->data = dm_malloc(msg->size);
|
|
||||||
size = msg->size;
|
|
||||||
bytes = 0;
|
|
||||||
header = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes != size) {
|
|
||||||
dm_free(msg->data);
|
|
||||||
msg->data = NULL;
|
|
||||||
}
|
|
||||||
return bytes == size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write message to daemon. */
|
|
||||||
static int _daemon_write(struct dm_event_fifos *fifos,
|
|
||||||
struct dm_event_daemon_message *msg)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
fd_set fds;
|
|
||||||
size_t bytes = 0;
|
|
||||||
size_t size = 2 * sizeof(uint32_t) + msg->size;
|
|
||||||
uint32_t *header = alloca(size);
|
|
||||||
char *buf = (char *)header;
|
|
||||||
char drainbuf[128];
|
|
||||||
|
|
||||||
header[0] = htonl(msg->cmd);
|
|
||||||
header[1] = htonl(msg->size);
|
|
||||||
memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
|
|
||||||
|
|
||||||
/* drain the answer fifo */
|
|
||||||
while (1) {
|
|
||||||
struct timeval tval = { .tv_usec = 100 };
|
|
||||||
FD_ZERO(&fds);
|
|
||||||
FD_SET(fifos->server, &fds);
|
|
||||||
ret = select(fifos->server + 1, &fds, NULL, NULL, &tval);
|
|
||||||
if (ret < 0) {
|
|
||||||
if (errno == EINTR)
|
|
||||||
continue;
|
|
||||||
log_error("Unable to talk to event daemon.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (ret == 0)
|
|
||||||
break;
|
|
||||||
ret = read(fifos->server, drainbuf, sizeof(drainbuf));
|
|
||||||
if (ret < 0) {
|
|
||||||
if ((errno == EINTR) || (errno == EAGAIN))
|
|
||||||
continue;
|
|
||||||
log_error("Unable to talk to event daemon.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (bytes < size) {
|
|
||||||
do {
|
|
||||||
/* Watch daemon write FIFO to be ready for output. */
|
|
||||||
FD_ZERO(&fds);
|
|
||||||
FD_SET(fifos->client, &fds);
|
|
||||||
ret = select(fifos->client + 1, NULL, &fds, NULL, NULL);
|
|
||||||
if ((ret < 0) && (errno != EINTR)) {
|
|
||||||
log_error("Unable to talk to event daemon.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} while (ret < 1);
|
|
||||||
|
|
||||||
ret = write(fifos->client, buf + bytes, size - bytes);
|
|
||||||
if (ret < 0) {
|
|
||||||
if ((errno == EINTR) || (errno == EAGAIN))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
log_error("Unable to talk to event daemon.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes == size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int daemon_talk(struct dm_event_fifos *fifos,
|
|
||||||
struct dm_event_daemon_message *msg, int cmd,
|
|
||||||
const char *dso_name, const char *dev_name,
|
|
||||||
enum dm_event_mask evmask, uint32_t timeout)
|
|
||||||
{
|
|
||||||
int msg_size;
|
|
||||||
memset(msg, 0, sizeof(*msg));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set command and pack the arguments
|
|
||||||
* into ASCII message string.
|
|
||||||
*/
|
|
||||||
if ((msg_size =
|
|
||||||
((cmd == DM_EVENT_CMD_HELLO) ?
|
|
||||||
dm_asprintf(&(msg->data), "%d:%d HELLO", getpid(), _sequence_nr) :
|
|
||||||
dm_asprintf(&(msg->data), "%d:%d %s %s %u %" PRIu32,
|
|
||||||
getpid(), _sequence_nr,
|
|
||||||
dso_name ? : "-", dev_name ? : "-", evmask, timeout)))
|
|
||||||
< 0) {
|
|
||||||
log_error("_daemon_talk: message allocation failed.");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
msg->cmd = cmd;
|
|
||||||
msg->size = msg_size;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Write command and message to and
|
|
||||||
* read status return code from daemon.
|
|
||||||
*/
|
|
||||||
if (!_daemon_write(fifos, msg)) {
|
|
||||||
stack;
|
|
||||||
dm_free(msg->data);
|
|
||||||
msg->data = NULL;
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
dm_free(msg->data);
|
|
||||||
msg->data = NULL;
|
|
||||||
|
|
||||||
if (!_daemon_read(fifos, msg)) {
|
|
||||||
stack;
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
} while (!_check_message_id(msg));
|
|
||||||
|
|
||||||
_sequence_nr++;
|
|
||||||
|
|
||||||
return (int32_t) msg->cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* start_daemon
|
|
||||||
*
|
|
||||||
* This function forks off a process (dmeventd) that will handle
|
|
||||||
* the events. I am currently test opening one of the fifos to
|
|
||||||
* ensure that the daemon is running and listening... I thought
|
|
||||||
* this would be less expensive than fork/exec'ing every time.
|
|
||||||
* Perhaps there is an even quicker/better way (no, checking the
|
|
||||||
* lock file is _not_ a better way).
|
|
||||||
*
|
|
||||||
* Returns: 1 on success, 0 otherwise
|
|
||||||
*/
|
|
||||||
static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos)
|
|
||||||
{
|
|
||||||
int pid, ret = 0;
|
|
||||||
int status;
|
|
||||||
struct stat statbuf;
|
|
||||||
char default_dmeventd_path[] = DMEVENTD_PATH;
|
|
||||||
char *args[] = { dmeventd_path ? : default_dmeventd_path, NULL };
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME Explicitly verify the code's requirement that client_path is secure:
|
|
||||||
* - All parent directories owned by root without group/other write access unless sticky.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* If client fifo path exists, only use it if it is root-owned fifo mode 0600 */
|
|
||||||
if ((lstat(fifos->client_path, &statbuf) < 0)) {
|
|
||||||
if (errno == ENOENT)
|
|
||||||
/* Jump ahead if fifo does not already exist. */
|
|
||||||
goto start_server;
|
|
||||||
else {
|
|
||||||
log_sys_error("stat", fifos->client_path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else if (!S_ISFIFO(statbuf.st_mode)) {
|
|
||||||
log_error("%s must be a fifo.", fifos->client_path);
|
|
||||||
return 0;
|
|
||||||
} else if (statbuf.st_uid) {
|
|
||||||
log_error("%s must be owned by uid 0.", fifos->client_path);
|
|
||||||
return 0;
|
|
||||||
} else if (statbuf.st_mode & (S_IEXEC | S_IRWXG | S_IRWXO)) {
|
|
||||||
log_error("%s must have mode 0600.", fifos->client_path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Anyone listening? If not, errno will be ENXIO */
|
|
||||||
fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK);
|
|
||||||
if (fifos->client >= 0) {
|
|
||||||
/* Should never happen if all the above checks passed. */
|
|
||||||
if ((fstat(fifos->client, &statbuf) < 0) ||
|
|
||||||
!S_ISFIFO(statbuf.st_mode) || statbuf.st_uid ||
|
|
||||||
(statbuf.st_mode & (S_IEXEC | S_IRWXG | S_IRWXO))) {
|
|
||||||
log_error("%s is no longer a secure root-owned fifo with mode 0600.", fifos->client_path);
|
|
||||||
if (close(fifos->client))
|
|
||||||
log_sys_debug("close", fifos->client_path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* server is running and listening */
|
|
||||||
if (close(fifos->client))
|
|
||||||
log_sys_debug("close", fifos->client_path);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (errno != ENXIO && errno != ENOENT) {
|
|
||||||
/* problem */
|
|
||||||
log_sys_error("open", fifos->client_path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
start_server:
|
|
||||||
/* server is not running */
|
|
||||||
|
|
||||||
if ((args[0][0] == '/') && stat(args[0], &statbuf)) {
|
|
||||||
log_sys_error("stat", args[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
|
|
||||||
if (pid < 0)
|
|
||||||
log_sys_error("fork", "");
|
|
||||||
|
|
||||||
else if (!pid) {
|
|
||||||
execvp(args[0], args);
|
|
||||||
log_error("Unable to exec dmeventd: %s.", strerror(errno));
|
|
||||||
_exit(EXIT_FAILURE);
|
|
||||||
} else {
|
|
||||||
if (waitpid(pid, &status, 0) < 0)
|
|
||||||
log_error("Unable to start dmeventd: %s.",
|
|
||||||
strerror(errno));
|
|
||||||
else if (WEXITSTATUS(status))
|
|
||||||
log_error("Unable to start dmeventd.");
|
|
||||||
else
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int init_fifos(struct dm_event_fifos *fifos)
|
|
||||||
{
|
|
||||||
/* FIXME? Is fifo the most suitable method? Why not share
|
|
||||||
comms/daemon code with something else e.g. multipath? */
|
|
||||||
|
|
||||||
/* Open the fifo used to read from the daemon. */
|
|
||||||
if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
|
|
||||||
log_sys_error("open", fifos->server_path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lock out anyone else trying to do communication with the daemon. */
|
|
||||||
if (flock(fifos->server, LOCK_EX) < 0) {
|
|
||||||
log_sys_error("flock", fifos->server_path);
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if ((fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK)) < 0) {*/
|
|
||||||
if ((fifos->client = open(fifos->client_path, O_RDWR | O_NONBLOCK)) < 0) {
|
|
||||||
log_sys_error("open", fifos->client_path);
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
bad:
|
|
||||||
if (close(fifos->server))
|
|
||||||
log_sys_debug("close", fifos->server_path);
|
|
||||||
fifos->server = -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize client. */
|
|
||||||
static int _init_client(char *dmeventd_path, struct dm_event_fifos *fifos)
|
|
||||||
{
|
|
||||||
if (!_start_daemon(dmeventd_path, fifos))
|
|
||||||
return_0;
|
|
||||||
|
|
||||||
return init_fifos(fifos);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fini_fifos(struct dm_event_fifos *fifos)
|
|
||||||
{
|
|
||||||
if (fifos->client >= 0 && close(fifos->client))
|
|
||||||
log_sys_debug("close", fifos->client_path);
|
|
||||||
|
|
||||||
if (fifos->server >= 0) {
|
|
||||||
if (flock(fifos->server, LOCK_UN))
|
|
||||||
log_sys_debug("flock unlock", fifos->server_path);
|
|
||||||
|
|
||||||
if (close(fifos->server))
|
|
||||||
log_sys_debug("close", fifos->server_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get uuid of a device */
|
|
||||||
static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
struct dm_task *dmt;
|
|
||||||
struct dm_info info;
|
|
||||||
|
|
||||||
if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
|
|
||||||
log_error("_get_device_info: dm_task creation for info failed.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dmevh->uuid) {
|
|
||||||
if (!dm_task_set_uuid(dmt, dmevh->uuid))
|
|
||||||
goto_bad;
|
|
||||||
} else if (dmevh->dev_name) {
|
|
||||||
if (!dm_task_set_name(dmt, dmevh->dev_name))
|
|
||||||
goto_bad;
|
|
||||||
} else if (dmevh->major && dmevh->minor) {
|
|
||||||
if (!dm_task_set_major(dmt, dmevh->major) ||
|
|
||||||
!dm_task_set_minor(dmt, dmevh->minor))
|
|
||||||
goto_bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME Add name or uuid or devno to messages */
|
|
||||||
if (!dm_task_run(dmt)) {
|
|
||||||
log_error("_get_device_info: dm_task_run() failed.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dm_task_get_info(dmt, &info)) {
|
|
||||||
log_error("_get_device_info: failed to get info for device.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!info.exists) {
|
|
||||||
log_error("_get_device_info: %s%s%s%.0d%s%.0d%s%s: device not found.",
|
|
||||||
dmevh->uuid ? : "",
|
|
||||||
(!dmevh->uuid && dmevh->dev_name) ? dmevh->dev_name : "",
|
|
||||||
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? "(" : "",
|
|
||||||
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? dmevh->major : 0,
|
|
||||||
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? ":" : "",
|
|
||||||
(!dmevh->uuid && !dmevh->dev_name && dmevh->minor > 0) ? dmevh->minor : 0,
|
|
||||||
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) && dmevh->minor == 0 ? "0" : "",
|
|
||||||
(!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? ") " : "");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dmt;
|
|
||||||
|
|
||||||
bad:
|
|
||||||
dm_task_destroy(dmt);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle the event (de)registration call and return negative error codes. */
|
|
||||||
static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_message *msg,
|
|
||||||
const char *dso_name, const char *dev_name,
|
|
||||||
enum dm_event_mask evmask, uint32_t timeout)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct dm_event_fifos fifos = {
|
|
||||||
.server = -1,
|
|
||||||
.client = -1,
|
|
||||||
/* FIXME Make these either configurable or depend directly on dmeventd_path */
|
|
||||||
.client_path = DM_EVENT_FIFO_CLIENT,
|
|
||||||
.server_path = DM_EVENT_FIFO_SERVER
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!_init_client(dmeventd_path, &fifos)) {
|
|
||||||
ret = -ESRCH;
|
|
||||||
goto_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
|
|
||||||
|
|
||||||
dm_free(msg->data);
|
|
||||||
msg->data = 0;
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
ret = daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
|
|
||||||
out:
|
|
||||||
/* what is the opposite of init? */
|
|
||||||
fini_fifos(&fifos);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* External library interface. */
|
|
||||||
int dm_event_register_handler(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
int ret = 1, err;
|
|
||||||
const char *uuid;
|
|
||||||
struct dm_task *dmt;
|
|
||||||
struct dm_event_daemon_message msg = { 0 };
|
|
||||||
|
|
||||||
if (!(dmt = _get_device_info(dmevh)))
|
|
||||||
return_0;
|
|
||||||
|
|
||||||
uuid = dm_task_get_uuid(dmt);
|
|
||||||
|
|
||||||
if (!strstr(dmevh->dso, "libdevmapper-event-lvm2thin.so") &&
|
|
||||||
!strstr(dmevh->dso, "libdevmapper-event-lvm2snapshot.so") &&
|
|
||||||
!strstr(dmevh->dso, "libdevmapper-event-lvm2mirror.so") &&
|
|
||||||
!strstr(dmevh->dso, "libdevmapper-event-lvm2raid.so"))
|
|
||||||
log_warn("WARNING: %s: dmeventd plugins are deprecated.", dmevh->dso);
|
|
||||||
|
|
||||||
|
|
||||||
if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
|
|
||||||
dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
|
|
||||||
log_error("%s: event registration failed: %s.",
|
|
||||||
dm_task_get_name(dmt),
|
|
||||||
msg.data ? msg.data : strerror(-err));
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_free(msg.data);
|
|
||||||
|
|
||||||
dm_task_destroy(dmt);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
|
|
||||||
{
|
|
||||||
int ret = 1, err;
|
|
||||||
const char *uuid;
|
|
||||||
struct dm_task *dmt;
|
|
||||||
struct dm_event_daemon_message msg = { 0 };
|
|
||||||
|
|
||||||
if (!(dmt = _get_device_info(dmevh)))
|
|
||||||
return_0;
|
|
||||||
|
|
||||||
uuid = dm_task_get_uuid(dmt);
|
|
||||||
|
|
||||||
if ((err = _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
|
|
||||||
dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
|
|
||||||
log_error("%s: event deregistration failed: %s.",
|
|
||||||
dm_task_get_name(dmt),
|
|
||||||
msg.data ? msg.data : strerror(-err));
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_free(msg.data);
|
|
||||||
|
|
||||||
dm_task_destroy(dmt);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fetch a string off src and duplicate it into *dest. */
|
|
||||||
/* FIXME: move to separate module to share with the daemon. */
|
|
||||||
static char *_fetch_string(char **src, const int delimiter)
|
|
||||||
{
|
|
||||||
char *p, *ret;
|
|
||||||
|
|
||||||
if ((p = strchr(*src, delimiter)))
|
|
||||||
*p = 0;
|
|
||||||
|
|
||||||
if ((ret = dm_strdup(*src)))
|
|
||||||
*src += strlen(ret) + 1;
|
|
||||||
|
|
||||||
if (p)
|
|
||||||
*p = delimiter;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse a device message from the daemon. */
|
|
||||||
static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
|
|
||||||
char **uuid, enum dm_event_mask *evmask)
|
|
||||||
{
|
|
||||||
char *id;
|
|
||||||
char *p = msg->data;
|
|
||||||
|
|
||||||
if ((id = _fetch_string(&p, ' ')) &&
|
|
||||||
(*dso_name = _fetch_string(&p, ' ')) &&
|
|
||||||
(*uuid = _fetch_string(&p, ' '))) {
|
|
||||||
*evmask = atoi(p);
|
|
||||||
dm_free(id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_free(id);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns 0 if handler found; error (-ENOMEM, -ENOENT) otherwise.
|
|
||||||
*/
|
|
||||||
int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
const char *uuid = NULL;
|
|
||||||
char *reply_dso = NULL, *reply_uuid = NULL;
|
|
||||||
enum dm_event_mask reply_mask = 0;
|
|
||||||
struct dm_task *dmt = NULL;
|
|
||||||
struct dm_event_daemon_message msg = { 0 };
|
|
||||||
struct dm_info info;
|
|
||||||
|
|
||||||
if (!(dmt = _get_device_info(dmevh))) {
|
|
||||||
log_debug("Device does not exists (uuid=%s, name=%s, %d:%d).",
|
|
||||||
dmevh->uuid, dmevh->dev_name,
|
|
||||||
dmevh->major, dmevh->minor);
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
uuid = dm_task_get_uuid(dmt);
|
|
||||||
|
|
||||||
/* FIXME Distinguish errors connecting to daemon */
|
|
||||||
if (_do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
|
|
||||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
|
|
||||||
&msg, dmevh->dso, uuid, dmevh->mask, 0)) {
|
|
||||||
log_debug("%s: device not registered.", dm_task_get_name(dmt));
|
|
||||||
ret = -ENOENT;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME this will probably horribly break if we get
|
|
||||||
ill-formatted reply */
|
|
||||||
ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask);
|
|
||||||
|
|
||||||
dm_task_destroy(dmt);
|
|
||||||
dmt = NULL;
|
|
||||||
|
|
||||||
dm_free(msg.data);
|
|
||||||
msg.data = NULL;
|
|
||||||
|
|
||||||
_dm_event_handler_clear_dev_info(dmevh);
|
|
||||||
if (!reply_uuid) {
|
|
||||||
ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(dmevh->uuid = dm_strdup(reply_uuid))) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(dmt = _get_device_info(dmevh))) {
|
|
||||||
ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_event_handler_set_dso(dmevh, reply_dso);
|
|
||||||
dm_event_handler_set_event_mask(dmevh, reply_mask);
|
|
||||||
|
|
||||||
dm_free(reply_dso);
|
|
||||||
reply_dso = NULL;
|
|
||||||
|
|
||||||
dm_free(reply_uuid);
|
|
||||||
reply_uuid = NULL;
|
|
||||||
|
|
||||||
if (!(dmevh->dev_name = dm_strdup(dm_task_get_name(dmt)))) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dm_task_get_info(dmt, &info)) {
|
|
||||||
ret = -1;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
dmevh->major = info.major;
|
|
||||||
dmevh->minor = info.minor;
|
|
||||||
|
|
||||||
dm_task_destroy(dmt);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
dm_free(msg.data);
|
|
||||||
dm_free(reply_dso);
|
|
||||||
dm_free(reply_uuid);
|
|
||||||
_dm_event_handler_clear_dev_info(dmevh);
|
|
||||||
if (dmt)
|
|
||||||
dm_task_destroy(dmt);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* You can (and have to) call this at the stage of the protocol where
|
|
||||||
* daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0)
|
|
||||||
*
|
|
||||||
* would be normally sent. This call will parse the version reply from
|
|
||||||
* dmeventd, in addition to above call. It is not safe to call this at any
|
|
||||||
* other place in the protocol.
|
|
||||||
*
|
|
||||||
* This is an internal function, not exposed in the public API.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int dm_event_get_version(struct dm_event_fifos *fifos, int *version) {
|
|
||||||
char *p;
|
|
||||||
struct dm_event_daemon_message msg = { 0 };
|
|
||||||
|
|
||||||
if (daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0))
|
|
||||||
return 0;
|
|
||||||
p = msg.data;
|
|
||||||
*version = 0;
|
|
||||||
|
|
||||||
if (!p || !(p = strchr(p, ' '))) /* Message ID */
|
|
||||||
return 0;
|
|
||||||
if (!(p = strchr(p + 1, ' '))) /* HELLO */
|
|
||||||
return 0;
|
|
||||||
if ((p = strchr(p + 1, ' '))) /* HELLO, once more */
|
|
||||||
*version = atoi(p);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dm_event_log_set(int debug_log_level, int use_syslog)
|
|
||||||
{
|
|
||||||
_debug_level = debug_log_level;
|
|
||||||
_use_syslog = use_syslog;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dm_event_log(const char *subsys, int level, const char *file,
|
|
||||||
int line, int dm_errno_or_class,
|
|
||||||
const char *format, va_list ap)
|
|
||||||
{
|
|
||||||
static int _abort_on_internal_errors = -1;
|
|
||||||
static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static time_t start = 0;
|
|
||||||
const char *indent = "";
|
|
||||||
FILE *stream = log_stderr(level) ? stderr : stdout;
|
|
||||||
int prio;
|
|
||||||
time_t now;
|
|
||||||
int log_with_debug = 0;
|
|
||||||
|
|
||||||
if (subsys[0] == '#') {
|
|
||||||
/* Subsystems starting with '#' are logged
|
|
||||||
* only when debugging is enabled. */
|
|
||||||
log_with_debug++;
|
|
||||||
subsys++;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (log_level(level)) {
|
|
||||||
case _LOG_DEBUG:
|
|
||||||
/* Never shown without -ddd */
|
|
||||||
if (_debug_level < 3)
|
|
||||||
return;
|
|
||||||
prio = LOG_DEBUG;
|
|
||||||
indent = " ";
|
|
||||||
break;
|
|
||||||
case _LOG_INFO:
|
|
||||||
if (log_with_debug && _debug_level < 2)
|
|
||||||
return;
|
|
||||||
prio = LOG_INFO;
|
|
||||||
indent = " ";
|
|
||||||
break;
|
|
||||||
case _LOG_NOTICE:
|
|
||||||
if (log_with_debug && _debug_level < 1)
|
|
||||||
return;
|
|
||||||
prio = LOG_NOTICE;
|
|
||||||
indent = " ";
|
|
||||||
break;
|
|
||||||
case _LOG_WARN:
|
|
||||||
prio = LOG_WARNING;
|
|
||||||
break;
|
|
||||||
case _LOG_ERR:
|
|
||||||
prio = LOG_ERR;
|
|
||||||
stream = stderr;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
prio = LOG_CRIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Serialize to keep lines readable */
|
|
||||||
pthread_mutex_lock(&_log_mutex);
|
|
||||||
|
|
||||||
if (_use_syslog) {
|
|
||||||
vsyslog(prio, format, ap);
|
|
||||||
} else {
|
|
||||||
now = time(NULL);
|
|
||||||
if (!start)
|
|
||||||
start = now;
|
|
||||||
now -= start;
|
|
||||||
if (_debug_level)
|
|
||||||
fprintf(stream, "[%2d:%02d] %8x:%-6s%s",
|
|
||||||
(int)now / 60, (int)now % 60,
|
|
||||||
// TODO: Maybe use shorter ID
|
|
||||||
// ((int)(pthread_self()) >> 6) & 0xffff,
|
|
||||||
(int)pthread_self(), subsys,
|
|
||||||
(_debug_level > 3) ? "" : indent);
|
|
||||||
if (_debug_level > 3)
|
|
||||||
fprintf(stream, "%28s:%4d %s", file, line, indent);
|
|
||||||
vfprintf(stream, _(format), ap);
|
|
||||||
fputc('\n', stream);
|
|
||||||
fflush(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&_log_mutex);
|
|
||||||
|
|
||||||
if (_abort_on_internal_errors < 0)
|
|
||||||
/* Set when env DM_ABORT_ON_INTERNAL_ERRORS is not "0" */
|
|
||||||
_abort_on_internal_errors =
|
|
||||||
strcmp(getenv("DM_ABORT_ON_INTERNAL_ERRORS") ? : "0", "0");
|
|
||||||
|
|
||||||
if (_abort_on_internal_errors &&
|
|
||||||
!strncmp(format, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1))
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0 /* left out for now */
|
|
||||||
|
|
||||||
static char *_skip_string(char *src, const int delimiter)
|
|
||||||
{
|
|
||||||
src = srtchr(src, delimiter);
|
|
||||||
if (src && *(src + 1))
|
|
||||||
return src + 1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_set_timeout(const char *device_path, uint32_t timeout)
|
|
||||||
{
|
|
||||||
struct dm_event_daemon_message msg = { 0 };
|
|
||||||
|
|
||||||
if (!device_exists(device_path))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
return _do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg,
|
|
||||||
NULL, device_path, 0, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct dm_event_daemon_message msg = { 0 };
|
|
||||||
|
|
||||||
if (!device_exists(device_path))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
if (!(ret = _do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
|
|
||||||
0, 0))) {
|
|
||||||
char *p = _skip_string(msg.data, ' ');
|
|
||||||
if (!p) {
|
|
||||||
log_error("Malformed reply from dmeventd '%s'.",
|
|
||||||
msg.data);
|
|
||||||
dm_free(msg.data);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
*timeout = atoi(p);
|
|
||||||
}
|
|
||||||
dm_free(msg.data);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of the device-mapper userspace tools.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note that this file is released only as part of a technology preview
|
|
||||||
* and its contents may change in future updates in ways that do not
|
|
||||||
* preserve compatibility.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef LIB_DMEVENT_H
|
|
||||||
#define LIB_DMEVENT_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event library interface.
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum dm_event_mask {
|
|
||||||
DM_EVENT_SETTINGS_MASK = 0x0000FF,
|
|
||||||
DM_EVENT_SINGLE = 0x000001, /* Report multiple errors just once. */
|
|
||||||
DM_EVENT_MULTI = 0x000002, /* Report all of them. */
|
|
||||||
|
|
||||||
DM_EVENT_ERROR_MASK = 0x00FF00,
|
|
||||||
DM_EVENT_SECTOR_ERROR = 0x000100, /* Failure on a particular sector. */
|
|
||||||
DM_EVENT_DEVICE_ERROR = 0x000200, /* Device failure. */
|
|
||||||
DM_EVENT_PATH_ERROR = 0x000400, /* Failure on an io path. */
|
|
||||||
DM_EVENT_ADAPTOR_ERROR = 0x000800, /* Failure of a host adaptor. */
|
|
||||||
|
|
||||||
DM_EVENT_STATUS_MASK = 0xFF0000,
|
|
||||||
DM_EVENT_SYNC_STATUS = 0x010000, /* Mirror synchronization completed/failed. */
|
|
||||||
DM_EVENT_TIMEOUT = 0x020000, /* Timeout has occured */
|
|
||||||
|
|
||||||
DM_EVENT_REGISTRATION_PENDING = 0x1000000, /* Monitor thread is setting-up/shutting-down */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DM_EVENT_ALL_ERRORS DM_EVENT_ERROR_MASK
|
|
||||||
#define DM_EVENT_PROTOCOL_VERSION 2
|
|
||||||
|
|
||||||
struct dm_task;
|
|
||||||
struct dm_event_handler;
|
|
||||||
|
|
||||||
struct dm_event_handler *dm_event_handler_create(void);
|
|
||||||
void dm_event_handler_destroy(struct dm_event_handler *dmevh);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Path of shared library to handle events.
|
|
||||||
*
|
|
||||||
* All of dmeventd, dso, device_name and uuid strings are duplicated so
|
|
||||||
* you do not need to keep the pointers valid after the call succeeds.
|
|
||||||
* They may return -ENOMEM though.
|
|
||||||
*/
|
|
||||||
int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Path of dmeventd binary.
|
|
||||||
*/
|
|
||||||
int dm_event_handler_set_dmeventd_path(struct dm_event_handler *dmevh, const char *dmeventd_path);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Identify the device to monitor by exactly one of device_name, uuid or
|
|
||||||
* device number. String arguments are duplicated, see above.
|
|
||||||
*/
|
|
||||||
int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *device_name);
|
|
||||||
|
|
||||||
int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid);
|
|
||||||
|
|
||||||
void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major);
|
|
||||||
void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor);
|
|
||||||
void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Specify mask for events to monitor.
|
|
||||||
*/
|
|
||||||
// FIXME misuse of bitmask as enum
|
|
||||||
void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
|
|
||||||
enum dm_event_mask evmask);
|
|
||||||
|
|
||||||
const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh);
|
|
||||||
const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh);
|
|
||||||
const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh);
|
|
||||||
int dm_event_handler_get_major(const struct dm_event_handler *dmevh);
|
|
||||||
int dm_event_handler_get_minor(const struct dm_event_handler *dmevh);
|
|
||||||
int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh);
|
|
||||||
// FIXME misuse of bitmask as enum
|
|
||||||
enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh);
|
|
||||||
|
|
||||||
/* FIXME Review interface (what about this next thing?) */
|
|
||||||
int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initiate monitoring using dmeventd.
|
|
||||||
*/
|
|
||||||
int dm_event_register_handler(const struct dm_event_handler *dmevh);
|
|
||||||
int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
|
|
||||||
|
|
||||||
/* Set debug level for logging, and whether to log on stdout/stderr or syslog */
|
|
||||||
void dm_event_log_set(int debug_log_level, int use_syslog);
|
|
||||||
|
|
||||||
/* Log messages acroding to current debug level */
|
|
||||||
__attribute__((format(printf, 6, 0)))
|
|
||||||
void dm_event_log(const char *subsys, int level, const char *file,
|
|
||||||
int line, int dm_errno_or_class,
|
|
||||||
const char *format, va_list ap);
|
|
||||||
/* Macro to route print_log do dm_event_log() */
|
|
||||||
#define DM_EVENT_LOG_FN(subsys) \
|
|
||||||
void print_log(int level, const char *file, int line, int dm_errno_or_class,\
|
|
||||||
const char *format, ...)\
|
|
||||||
{\
|
|
||||||
va_list ap;\
|
|
||||||
va_start(ap, format);\
|
|
||||||
dm_event_log(subsys, level, file, line, dm_errno_or_class, format, ap);\
|
|
||||||
va_end(ap);\
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prototypes for DSO interface, see dmeventd.c, struct dso_data for
|
|
||||||
detailed descriptions. */
|
|
||||||
// FIXME misuse of bitmask as enum
|
|
||||||
void process_event(struct dm_task *dmt, enum dm_event_mask evmask, void **user);
|
|
||||||
int register_device(const char *device_name, const char *uuid, int major, int minor, void **user);
|
|
||||||
int unregister_device(const char *device_name, const char *uuid, int major,
|
|
||||||
int minor, void **user);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
prefix=@prefix@
|
|
||||||
exec_prefix=@exec_prefix@
|
|
||||||
libdir=@libdir@
|
|
||||||
includedir=@includedir@
|
|
||||||
|
|
||||||
Name: devmapper-event
|
|
||||||
Description: device-mapper event library
|
|
||||||
Version: @DM_LIB_PATCHLEVEL@
|
|
||||||
Cflags: -I${includedir}
|
|
||||||
Libs: -L${libdir} -ldevmapper-event
|
|
||||||
Requires.private: devmapper
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
# Copyright (C) 2004-2005, 2011 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
SUBDIRS += lvm2
|
|
||||||
|
|
||||||
ifneq ("@MIRRORS@", "none")
|
|
||||||
SUBDIRS += mirror
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ("@SNAPSHOTS@", "none")
|
|
||||||
SUBDIRS += snapshot
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ("@RAID@", "none")
|
|
||||||
SUBDIRS += raid
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ("@THIN@", "none")
|
|
||||||
SUBDIRS += thin
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(MAKECMDGOALS),distclean)
|
|
||||||
SUBDIRS = lvm2 mirror snapshot raid thin
|
|
||||||
endif
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
snapshot: lvm2
|
|
||||||
mirror: lvm2
|
|
||||||
raid: lvm2
|
|
||||||
thin: lvm2
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
dmeventd_lvm2_init
|
|
||||||
dmeventd_lvm2_exit
|
|
||||||
dmeventd_lvm2_lock
|
|
||||||
dmeventd_lvm2_unlock
|
|
||||||
dmeventd_lvm2_pool
|
|
||||||
dmeventd_lvm2_run
|
|
||||||
dmeventd_lvm2_command
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2010-2014 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
CLDFLAGS += -L$(top_builddir)/tools
|
|
||||||
|
|
||||||
SOURCES = dmeventd_lvm.c
|
|
||||||
|
|
||||||
LIB_SHARED = libdevmapper-event-lvm2.$(LIB_SUFFIX)
|
|
||||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
LIBS += @LVM2CMD_LIB@ -ldevmapper $(PTHREAD_LIBS)
|
|
||||||
|
|
||||||
install_lvm2: install_lib_shared
|
|
||||||
|
|
||||||
install: install_lvm2
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lib.h"
|
|
||||||
#include "dmeventd_lvm.h"
|
|
||||||
#include "libdevmapper-event.h"
|
|
||||||
#include "lvm2cmd.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* register_device() is called first and performs initialisation.
|
|
||||||
* Only one device may be registered or unregistered at a time.
|
|
||||||
*/
|
|
||||||
static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Number of active registrations.
|
|
||||||
*/
|
|
||||||
static int _register_count = 0;
|
|
||||||
static struct dm_pool *_mem_pool = NULL;
|
|
||||||
static void *_lvm_handle = NULL;
|
|
||||||
|
|
||||||
DM_EVENT_LOG_FN("#lvm")
|
|
||||||
|
|
||||||
static void _lvm2_print_log(int level, const char *file, int line,
|
|
||||||
int dm_errno_or_class, const char *msg)
|
|
||||||
{
|
|
||||||
print_log(level, file, line, dm_errno_or_class, "%s", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Currently only one event can be processed at a time.
|
|
||||||
*/
|
|
||||||
static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
void dmeventd_lvm2_lock(void)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&_event_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dmeventd_lvm2_unlock(void)
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock(&_event_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dmeventd_lvm2_init(void)
|
|
||||||
{
|
|
||||||
int r = 0;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&_register_mutex);
|
|
||||||
|
|
||||||
if (!_lvm_handle) {
|
|
||||||
lvm2_log_fn(_lvm2_print_log);
|
|
||||||
|
|
||||||
if (!(_lvm_handle = lvm2_init()))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Need some space for allocations. 1024 should be more
|
|
||||||
* than enough for what we need (device mapper name splitting)
|
|
||||||
*/
|
|
||||||
if (!_mem_pool && !(_mem_pool = dm_pool_create("mirror_dso", 1024))) {
|
|
||||||
lvm2_exit(_lvm_handle);
|
|
||||||
_lvm_handle = NULL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
lvm2_disable_dmeventd_monitoring(_lvm_handle);
|
|
||||||
/* FIXME Temporary: move to dmeventd core */
|
|
||||||
lvm2_run(_lvm_handle, "_memlock_inc");
|
|
||||||
log_debug("lvm plugin initilized.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_register_count++;
|
|
||||||
r = 1;
|
|
||||||
|
|
||||||
out:
|
|
||||||
pthread_mutex_unlock(&_register_mutex);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dmeventd_lvm2_exit(void)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&_register_mutex);
|
|
||||||
|
|
||||||
if (!--_register_count) {
|
|
||||||
log_debug("lvm plugin shuting down.");
|
|
||||||
lvm2_run(_lvm_handle, "_memlock_dec");
|
|
||||||
dm_pool_destroy(_mem_pool);
|
|
||||||
_mem_pool = NULL;
|
|
||||||
lvm2_exit(_lvm_handle);
|
|
||||||
_lvm_handle = NULL;
|
|
||||||
log_debug("lvm plugin exited.");
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&_register_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dm_pool *dmeventd_lvm2_pool(void)
|
|
||||||
{
|
|
||||||
return _mem_pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dmeventd_lvm2_run(const char *cmdline)
|
|
||||||
{
|
|
||||||
return (lvm2_run(_lvm_handle, cmdline) == LVM2_COMMAND_SUCCEEDED);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
|
||||||
const char *cmd, const char *device)
|
|
||||||
{
|
|
||||||
static char _internal_prefix[] = "_dmeventd_";
|
|
||||||
char *vg = NULL, *lv = NULL, *layer;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (!dm_split_lvm_name(mem, device, &vg, &lv, &layer)) {
|
|
||||||
log_error("Unable to determine VG name from %s.",
|
|
||||||
device);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* strip off the mirror component designations */
|
|
||||||
if ((layer = strstr(lv, "_mimagetmp")) ||
|
|
||||||
(layer = strstr(lv, "_mlog")))
|
|
||||||
*layer = '\0';
|
|
||||||
|
|
||||||
if (!strncmp(cmd, _internal_prefix, sizeof(_internal_prefix) - 1)) {
|
|
||||||
dmeventd_lvm2_lock();
|
|
||||||
/* output of internal command passed via env var */
|
|
||||||
if (!dmeventd_lvm2_run(cmd))
|
|
||||||
cmd = NULL;
|
|
||||||
else if ((cmd = getenv(cmd)))
|
|
||||||
cmd = dm_pool_strdup(mem, cmd); /* copy with lock */
|
|
||||||
dmeventd_lvm2_unlock();
|
|
||||||
|
|
||||||
if (!cmd) {
|
|
||||||
log_error("Unable to find configured command.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv);
|
|
||||||
|
|
||||||
dm_pool_free(mem, vg);
|
|
||||||
|
|
||||||
if (r < 0) {
|
|
||||||
log_error("Unable to form LVM command. (too long).");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wrappers around liblvm2cmd functions for dmeventd plug-ins.
|
|
||||||
*
|
|
||||||
* liblvm2cmd is not thread-safe so the locking in this library helps dmeventd
|
|
||||||
* threads to co-operate in sharing a single instance.
|
|
||||||
*
|
|
||||||
* FIXME Either support this properly as a generic liblvm2cmd wrapper or make
|
|
||||||
* liblvm2cmd thread-safe so this can go away.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _DMEVENTD_LVMWRAP_H
|
|
||||||
#define _DMEVENTD_LVMWRAP_H
|
|
||||||
|
|
||||||
struct dm_pool;
|
|
||||||
|
|
||||||
int dmeventd_lvm2_init(void);
|
|
||||||
void dmeventd_lvm2_exit(void);
|
|
||||||
int dmeventd_lvm2_run(const char *cmdline);
|
|
||||||
|
|
||||||
void dmeventd_lvm2_lock(void);
|
|
||||||
void dmeventd_lvm2_unlock(void);
|
|
||||||
|
|
||||||
struct dm_pool *dmeventd_lvm2_pool(void);
|
|
||||||
|
|
||||||
int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
|
||||||
const char *cmd, const char *device);
|
|
||||||
|
|
||||||
#define dmeventd_lvm2_run_with_lock(cmdline) \
|
|
||||||
({\
|
|
||||||
int rc;\
|
|
||||||
dmeventd_lvm2_lock();\
|
|
||||||
rc = dmeventd_lvm2_run(cmdline);\
|
|
||||||
dmeventd_lvm2_unlock();\
|
|
||||||
rc;\
|
|
||||||
})
|
|
||||||
|
|
||||||
#define dmeventd_lvm2_init_with_pool(name, st) \
|
|
||||||
({\
|
|
||||||
struct dm_pool *mem;\
|
|
||||||
st = NULL;\
|
|
||||||
if (dmeventd_lvm2_init()) {\
|
|
||||||
if ((mem = dm_pool_create(name, 2048)) &&\
|
|
||||||
(st = dm_pool_zalloc(mem, sizeof(*st))))\
|
|
||||||
st->mem = mem;\
|
|
||||||
else {\
|
|
||||||
if (mem)\
|
|
||||||
dm_pool_destroy(mem);\
|
|
||||||
dmeventd_lvm2_exit();\
|
|
||||||
}\
|
|
||||||
}\
|
|
||||||
st;\
|
|
||||||
})
|
|
||||||
|
|
||||||
#define dmeventd_lvm2_exit_with_pool(pool) \
|
|
||||||
do {\
|
|
||||||
dm_pool_destroy(pool->mem);\
|
|
||||||
dmeventd_lvm2_exit();\
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#endif /* _DMEVENTD_LVMWRAP_H */
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
process_event
|
|
||||||
register_device
|
|
||||||
unregister_device
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
# Copyright (C) 2004-2005, 2008-2014 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
|
|
||||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
|
||||||
|
|
||||||
SOURCES = dmeventd_mirror.c
|
|
||||||
|
|
||||||
LIB_NAME = libdevmapper-event-lvm2mirror
|
|
||||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
|
||||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
|
||||||
|
|
||||||
CFLOW_LIST = $(SOURCES)
|
|
||||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
LIBS += -ldevmapper-event-lvm2 -ldevmapper
|
|
||||||
|
|
||||||
install_lvm2: install_dm_plugin
|
|
||||||
|
|
||||||
install: install_lvm2
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lib.h"
|
|
||||||
#include "libdevmapper-event.h"
|
|
||||||
#include "dmeventd_lvm.h"
|
|
||||||
#include "activate.h" /* For TARGET_NAME* */
|
|
||||||
|
|
||||||
/* FIXME Reformat to 80 char lines. */
|
|
||||||
|
|
||||||
#define ME_IGNORE 0
|
|
||||||
#define ME_INSYNC 1
|
|
||||||
#define ME_FAILURE 2
|
|
||||||
|
|
||||||
struct dso_state {
|
|
||||||
struct dm_pool *mem;
|
|
||||||
char cmd_lvconvert[512];
|
|
||||||
};
|
|
||||||
|
|
||||||
DM_EVENT_LOG_FN("mirr")
|
|
||||||
|
|
||||||
static void _process_status_code(dm_status_mirror_health_t health,
|
|
||||||
uint32_t major, uint32_t minor,
|
|
||||||
const char *dev_type, int *r)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* A => Alive - No failures
|
|
||||||
* D => Dead - A write failure occurred leaving mirror out-of-sync
|
|
||||||
* F => Flush failed.
|
|
||||||
* S => Sync - A sychronization failure occurred, mirror out-of-sync
|
|
||||||
* R => Read - A read failure occurred, mirror data unaffected
|
|
||||||
* U => Unclassified failure (bug)
|
|
||||||
*/
|
|
||||||
switch (health) {
|
|
||||||
case DM_STATUS_MIRROR_ALIVE:
|
|
||||||
return;
|
|
||||||
case DM_STATUS_MIRROR_FLUSH_FAILED:
|
|
||||||
log_error("%s device %u:%u flush failed.",
|
|
||||||
dev_type, major, minor);
|
|
||||||
*r = ME_FAILURE;
|
|
||||||
break;
|
|
||||||
case DM_STATUS_MIRROR_SYNC_FAILED:
|
|
||||||
log_error("%s device %u:%u sync failed.",
|
|
||||||
dev_type, major, minor);
|
|
||||||
break;
|
|
||||||
case DM_STATUS_MIRROR_READ_FAILED:
|
|
||||||
log_error("%s device %u:%u read failed.",
|
|
||||||
dev_type, major, minor);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
log_error("%s device %u:%u has failed (%c).",
|
|
||||||
dev_type, major, minor, (char)health);
|
|
||||||
*r = ME_FAILURE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _get_mirror_event(struct dso_state *state, char *params)
|
|
||||||
{
|
|
||||||
int r = ME_INSYNC;
|
|
||||||
unsigned i;
|
|
||||||
struct dm_status_mirror *ms;
|
|
||||||
|
|
||||||
if (!dm_get_status_mirror(state->mem, params, &ms)) {
|
|
||||||
log_error("Unable to parse mirror status string.");
|
|
||||||
return ME_IGNORE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for bad mirror devices */
|
|
||||||
for (i = 0; i < ms->dev_count; ++i)
|
|
||||||
_process_status_code(ms->devs[i].health,
|
|
||||||
ms->devs[i].major, ms->devs[i].minor,
|
|
||||||
i ? "Secondary mirror" : "Primary mirror", &r);
|
|
||||||
|
|
||||||
/* Check for bad disk log device */
|
|
||||||
for (i = 0; i < ms->log_count; ++i)
|
|
||||||
_process_status_code(ms->logs[i].health,
|
|
||||||
ms->logs[i].major, ms->logs[i].minor,
|
|
||||||
"Log", &r);
|
|
||||||
|
|
||||||
/* Ignore if not in-sync */
|
|
||||||
if ((r == ME_INSYNC) && (ms->insync_regions != ms->total_regions))
|
|
||||||
r = ME_IGNORE;
|
|
||||||
|
|
||||||
dm_pool_free(state->mem, ms);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _remove_failed_devices(const char *cmd_lvconvert, const char *device)
|
|
||||||
{
|
|
||||||
/* if repair goes OK, report success even if lvscan has failed */
|
|
||||||
if (!dmeventd_lvm2_run_with_lock(cmd_lvconvert)) {
|
|
||||||
log_error("Repair of mirrored device %s failed.", device);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_info("Repair of mirrored device %s finished successfully.", device);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_event(struct dm_task *dmt,
|
|
||||||
enum dm_event_mask event __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state = *user;
|
|
||||||
void *next = NULL;
|
|
||||||
uint64_t start, length;
|
|
||||||
char *target_type = NULL;
|
|
||||||
char *params;
|
|
||||||
const char *device = dm_task_get_name(dmt);
|
|
||||||
|
|
||||||
do {
|
|
||||||
next = dm_get_next_target(dmt, next, &start, &length,
|
|
||||||
&target_type, ¶ms);
|
|
||||||
|
|
||||||
if (!target_type) {
|
|
||||||
log_info("%s mapping lost.", device);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(target_type, TARGET_NAME_MIRROR)) {
|
|
||||||
log_info("%s has unmirrored portion.", device);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(_get_mirror_event(state, params)) {
|
|
||||||
case ME_INSYNC:
|
|
||||||
/* FIXME: all we really know is that this
|
|
||||||
_part_ of the device is in sync
|
|
||||||
Also, this is not an error
|
|
||||||
*/
|
|
||||||
log_notice("%s is now in-sync.", device);
|
|
||||||
break;
|
|
||||||
case ME_FAILURE:
|
|
||||||
log_error("Device failure in %s.", device);
|
|
||||||
if (!_remove_failed_devices(state->cmd_lvconvert, device))
|
|
||||||
/* FIXME Why are all the error return codes unused? Get rid of them? */
|
|
||||||
log_error("Failed to remove faulty devices in %s.",
|
|
||||||
device);
|
|
||||||
/* Should check before warning user that device is now linear
|
|
||||||
else
|
|
||||||
log_notice("%s is now a linear device.",
|
|
||||||
device);
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
case ME_IGNORE:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* FIXME Provide value then! */
|
|
||||||
log_warn("WARNING: %s received unknown event.", device);
|
|
||||||
}
|
|
||||||
} while (next);
|
|
||||||
}
|
|
||||||
|
|
||||||
int register_device(const char *device,
|
|
||||||
const char *uuid __attribute__((unused)),
|
|
||||||
int major __attribute__((unused)),
|
|
||||||
int minor __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state;
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_init_with_pool("mirror_state", state))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
/* CANNOT use --config as this disables cached content */
|
|
||||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
|
|
||||||
"lvconvert --repair --use-policies", device))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
*user = state;
|
|
||||||
|
|
||||||
log_info("Monitoring mirror device %s for events.", device);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
bad:
|
|
||||||
log_error("Failed to monitor mirror %s.", device);
|
|
||||||
|
|
||||||
if (state)
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int unregister_device(const char *device,
|
|
||||||
const char *uuid __attribute__((unused)),
|
|
||||||
int major __attribute__((unused)),
|
|
||||||
int minor __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state = *user;
|
|
||||||
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
log_info("No longer monitoring mirror device %s for events.",
|
|
||||||
device);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
process_event
|
|
||||||
register_device
|
|
||||||
unregister_device
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2011-2014 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
|
|
||||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
|
||||||
|
|
||||||
SOURCES = dmeventd_raid.c
|
|
||||||
|
|
||||||
LIB_NAME = libdevmapper-event-lvm2raid
|
|
||||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
|
||||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
|
||||||
|
|
||||||
CFLOW_LIST = $(SOURCES)
|
|
||||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
LIBS += -ldevmapper-event-lvm2 -ldevmapper
|
|
||||||
|
|
||||||
install_lvm2: install_dm_plugin
|
|
||||||
|
|
||||||
install: install_lvm2
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lib.h"
|
|
||||||
#include "defaults.h"
|
|
||||||
#include "dmeventd_lvm.h"
|
|
||||||
#include "libdevmapper-event.h"
|
|
||||||
|
|
||||||
/* Hold enough elements for the mximum number of RAID images */
|
|
||||||
#define RAID_DEVS_ELEMS ((DEFAULT_RAID_MAX_IMAGES + 63) / 64)
|
|
||||||
|
|
||||||
struct dso_state {
|
|
||||||
struct dm_pool *mem;
|
|
||||||
char cmd_lvconvert[512];
|
|
||||||
uint64_t raid_devs[RAID_DEVS_ELEMS];
|
|
||||||
int failed;
|
|
||||||
int warned;
|
|
||||||
};
|
|
||||||
|
|
||||||
DM_EVENT_LOG_FN("raid")
|
|
||||||
|
|
||||||
/* FIXME Reformat to 80 char lines. */
|
|
||||||
|
|
||||||
static int _process_raid_event(struct dso_state *state, char *params, const char *device)
|
|
||||||
{
|
|
||||||
struct dm_status_raid *status;
|
|
||||||
const char *d;
|
|
||||||
int dead = 0, r = 1;
|
|
||||||
uint32_t dev;
|
|
||||||
|
|
||||||
if (!dm_get_status_raid(state->mem, params, &status)) {
|
|
||||||
log_error("Failed to process status line for %s.", device);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
d = status->dev_health;
|
|
||||||
while ((d = strchr(d, 'D'))) {
|
|
||||||
dev = (uint32_t)(d - status->dev_health);
|
|
||||||
|
|
||||||
if (!(state->raid_devs[dev / 64] & (UINT64_C(1) << (dev % 64)))) {
|
|
||||||
state->raid_devs[dev / 64] |= (UINT64_C(1) << (dev % 64));
|
|
||||||
log_warn("WARNING: Device #%u of %s array, %s, has failed.",
|
|
||||||
dev, status->raid_type, device);
|
|
||||||
}
|
|
||||||
|
|
||||||
d++;
|
|
||||||
dead = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if we are converting from non-RAID to RAID (e.g. linear -> raid1)
|
|
||||||
* and too many original devices die, such that we cannot continue
|
|
||||||
* the "recover" operation, the sync action will go to "idle", the
|
|
||||||
* unsynced devs will remain at 'a', and the original devices will
|
|
||||||
* NOT SWITCH TO 'D', but will remain at 'A' - hoping to be revived.
|
|
||||||
*
|
|
||||||
* This is simply the way the kernel works...
|
|
||||||
*/
|
|
||||||
if (!strcmp(status->sync_action, "idle") &&
|
|
||||||
(status->dev_health[0] == 'a') &&
|
|
||||||
(status->insync_regions < status->total_regions)) {
|
|
||||||
log_error("Primary sources for new RAID, %s, have failed.",
|
|
||||||
device);
|
|
||||||
dead = 1; /* run it through LVM repair */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dead) {
|
|
||||||
if (status->insync_regions < status->total_regions) {
|
|
||||||
if (!state->warned) {
|
|
||||||
state->warned = 1;
|
|
||||||
log_warn("WARNING: waiting for resynchronization to finish "
|
|
||||||
"before initiating repair on RAID device %s.", device);
|
|
||||||
}
|
|
||||||
|
|
||||||
goto out; /* Not yet done syncing with accessible devices */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->failed)
|
|
||||||
goto out; /* already reported */
|
|
||||||
|
|
||||||
state->failed = 1;
|
|
||||||
|
|
||||||
/* if repair goes OK, report success even if lvscan has failed */
|
|
||||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) {
|
|
||||||
log_error("Repair of RAID device %s failed.", device);
|
|
||||||
r = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state->failed = 0;
|
|
||||||
if (status->insync_regions == status->total_regions)
|
|
||||||
memset(&state->raid_devs, 0, sizeof(state->raid_devs));
|
|
||||||
log_info("%s array, %s, is %s in-sync.",
|
|
||||||
status->raid_type, device,
|
|
||||||
(status->insync_regions == status->total_regions) ? "now" : "not");
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
dm_pool_free(state->mem, status);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_event(struct dm_task *dmt,
|
|
||||||
enum dm_event_mask event __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state = *user;
|
|
||||||
void *next = NULL;
|
|
||||||
uint64_t start, length;
|
|
||||||
char *target_type = NULL;
|
|
||||||
char *params;
|
|
||||||
const char *device = dm_task_get_name(dmt);
|
|
||||||
|
|
||||||
do {
|
|
||||||
next = dm_get_next_target(dmt, next, &start, &length,
|
|
||||||
&target_type, ¶ms);
|
|
||||||
|
|
||||||
if (!target_type) {
|
|
||||||
log_info("%s mapping lost.", device);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(target_type, "raid")) {
|
|
||||||
log_info("%s has non-raid portion.", device);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_process_raid_event(state, params, device))
|
|
||||||
log_error("Failed to process event for %s.",
|
|
||||||
device);
|
|
||||||
} while (next);
|
|
||||||
}
|
|
||||||
|
|
||||||
int register_device(const char *device,
|
|
||||||
const char *uuid __attribute__((unused)),
|
|
||||||
int major __attribute__((unused)),
|
|
||||||
int minor __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state;
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_init_with_pool("raid_state", state))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
|
|
||||||
"lvconvert --repair --use-policies", device))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
*user = state;
|
|
||||||
|
|
||||||
log_info("Monitoring RAID device %s for events.", device);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
bad:
|
|
||||||
log_error("Failed to monitor RAID %s.", device);
|
|
||||||
|
|
||||||
if (state)
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int unregister_device(const char *device,
|
|
||||||
const char *uuid __attribute__((unused)),
|
|
||||||
int major __attribute__((unused)),
|
|
||||||
int minor __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state = *user;
|
|
||||||
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
log_info("No longer monitoring RAID device %s for events.",
|
|
||||||
device);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
process_event
|
|
||||||
register_device
|
|
||||||
unregister_device
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
|
||||||
# Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of the LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
|
|
||||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
|
||||||
|
|
||||||
SOURCES = dmeventd_snapshot.c
|
|
||||||
|
|
||||||
LIB_SHARED = libdevmapper-event-lvm2snapshot.$(LIB_SUFFIX)
|
|
||||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
LIBS += -ldevmapper-event-lvm2 -ldevmapper
|
|
||||||
|
|
||||||
install_lvm2: install_dm_plugin
|
|
||||||
|
|
||||||
install: install_lvm2
|
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007-2015 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lib.h"
|
|
||||||
#include "dmeventd_lvm.h"
|
|
||||||
#include "libdevmapper-event.h"
|
|
||||||
|
|
||||||
#include <sys/sysmacros.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
/* First warning when snapshot is 80% full. */
|
|
||||||
#define WARNING_THRESH (DM_PERCENT_1 * 80)
|
|
||||||
/* Run a check every 5%. */
|
|
||||||
#define CHECK_STEP (DM_PERCENT_1 * 5)
|
|
||||||
/* Do not bother checking snapshots less than 50% full. */
|
|
||||||
#define CHECK_MINIMUM (DM_PERCENT_1 * 50)
|
|
||||||
|
|
||||||
#define UMOUNT_COMMAND "/bin/umount"
|
|
||||||
|
|
||||||
struct dso_state {
|
|
||||||
struct dm_pool *mem;
|
|
||||||
dm_percent_t percent_check;
|
|
||||||
uint64_t known_size;
|
|
||||||
char cmd_lvextend[512];
|
|
||||||
};
|
|
||||||
|
|
||||||
DM_EVENT_LOG_FN("snap")
|
|
||||||
|
|
||||||
static int _run(const char *cmd, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
int argc = 1; /* for argv[0], i.e. cmd */
|
|
||||||
int i = 0;
|
|
||||||
const char **argv;
|
|
||||||
pid_t pid = fork();
|
|
||||||
int status;
|
|
||||||
|
|
||||||
if (pid == 0) { /* child */
|
|
||||||
va_start(ap, cmd);
|
|
||||||
while (va_arg(ap, const char *))
|
|
||||||
++ argc;
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
/* + 1 for the terminating NULL */
|
|
||||||
argv = alloca(sizeof(const char *) * (argc + 1));
|
|
||||||
|
|
||||||
argv[0] = cmd;
|
|
||||||
va_start(ap, cmd);
|
|
||||||
while ((argv[++i] = va_arg(ap, const char *)));
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
execvp(cmd, (char **)argv);
|
|
||||||
log_sys_error("exec", cmd);
|
|
||||||
exit(127);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid > 0) { /* parent */
|
|
||||||
if (waitpid(pid, &status, 0) != pid)
|
|
||||||
return 0; /* waitpid failed */
|
|
||||||
if (!WIFEXITED(status) || WEXITSTATUS(status))
|
|
||||||
return 0; /* the child failed */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid < 0)
|
|
||||||
return 0; /* fork failed */
|
|
||||||
|
|
||||||
return 1; /* all good */
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _extend(const char *cmd)
|
|
||||||
{
|
|
||||||
log_debug("Extending snapshot via %s.", cmd);
|
|
||||||
return dmeventd_lvm2_run_with_lock(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SNAPSHOT_REMOVE
|
|
||||||
/* Remove invalid snapshot from dm-table */
|
|
||||||
/* Experimental for now and not used by default */
|
|
||||||
static int _remove(const char *uuid)
|
|
||||||
{
|
|
||||||
int r = 1;
|
|
||||||
uint32_t cookie = 0;
|
|
||||||
struct dm_task *dmt;
|
|
||||||
|
|
||||||
if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!dm_task_set_uuid(dmt, uuid)) {
|
|
||||||
r = 0;
|
|
||||||
goto_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_task_retry_remove(dmt);
|
|
||||||
|
|
||||||
if (!dm_task_set_cookie(dmt, &cookie, 0)) {
|
|
||||||
r = 0;
|
|
||||||
goto_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dm_task_run(dmt)) {
|
|
||||||
r = 0;
|
|
||||||
goto_out;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
dm_task_destroy(dmt);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
#endif /* SNAPSHOT_REMOVE */
|
|
||||||
|
|
||||||
static void _umount(const char *device, int major, int minor)
|
|
||||||
{
|
|
||||||
FILE *mounts;
|
|
||||||
char buffer[4096];
|
|
||||||
char *words[3];
|
|
||||||
struct stat st;
|
|
||||||
const char procmounts[] = "/proc/mounts";
|
|
||||||
|
|
||||||
if (!(mounts = fopen(procmounts, "r"))) {
|
|
||||||
log_sys_error("fopen", procmounts);
|
|
||||||
log_error("Not umounting %s.", device);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!feof(mounts)) {
|
|
||||||
/* read a line of /proc/mounts */
|
|
||||||
if (!fgets(buffer, sizeof(buffer), mounts))
|
|
||||||
break; /* eof, likely */
|
|
||||||
|
|
||||||
/* words[0] is the mount point and words[1] is the device path */
|
|
||||||
if (dm_split_words(buffer, 3, 0, words) < 2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* find the major/minor of the device */
|
|
||||||
if (stat(words[0], &st))
|
|
||||||
continue; /* can't stat, skip this one */
|
|
||||||
|
|
||||||
if (S_ISBLK(st.st_mode) &&
|
|
||||||
(int) major(st.st_rdev) == major &&
|
|
||||||
(int) minor(st.st_rdev) == minor) {
|
|
||||||
log_error("Unmounting invalid snapshot %s from %s.", device, words[1]);
|
|
||||||
if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
|
|
||||||
log_error("Failed to umount snapshot %s from %s: %s.",
|
|
||||||
device, words[1], strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fclose(mounts))
|
|
||||||
log_sys_error("close", procmounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_event(struct dm_task *dmt,
|
|
||||||
enum dm_event_mask event __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state = *user;
|
|
||||||
void *next = NULL;
|
|
||||||
uint64_t start, length;
|
|
||||||
char *target_type = NULL;
|
|
||||||
char *params;
|
|
||||||
struct dm_status_snapshot *status = NULL;
|
|
||||||
const char *device = dm_task_get_name(dmt);
|
|
||||||
int percent;
|
|
||||||
struct dm_info info;
|
|
||||||
|
|
||||||
/* No longer monitoring, waiting for remove */
|
|
||||||
if (!state->percent_check)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
|
||||||
if (!target_type || strcmp(target_type, "snapshot")) {
|
|
||||||
log_error("Target %s is not snapshot.", target_type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dm_get_status_snapshot(state->mem, params, &status)) {
|
|
||||||
log_error("Cannot parse snapshot %s state: %s.", device, params);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the snapshot has been invalidated or we failed to parse
|
|
||||||
* the status string. Report the full status string to syslog.
|
|
||||||
*/
|
|
||||||
if (status->invalid || status->overflow || !status->total_sectors) {
|
|
||||||
log_warn("WARNING: Snapshot %s changed state to: %s and should be removed.",
|
|
||||||
device, params);
|
|
||||||
state->percent_check = 0;
|
|
||||||
if (dm_task_get_info(dmt, &info))
|
|
||||||
_umount(device, info.major, info.minor);
|
|
||||||
#ifdef SNAPSHOT_REMOVE
|
|
||||||
/* Maybe configurable ? */
|
|
||||||
_remove(dm_task_get_uuid(dmt));
|
|
||||||
#endif
|
|
||||||
pthread_kill(pthread_self(), SIGALRM);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length <= (status->used_sectors - status->metadata_sectors)) {
|
|
||||||
/* TODO eventually recognize earlier when room is enough */
|
|
||||||
log_info("Dropping monitoring of fully provisioned snapshot %s.",
|
|
||||||
device);
|
|
||||||
pthread_kill(pthread_self(), SIGALRM);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Snapshot size had changed. Clear the threshold. */
|
|
||||||
if (state->known_size != status->total_sectors) {
|
|
||||||
state->percent_check = CHECK_MINIMUM;
|
|
||||||
state->known_size = status->total_sectors;
|
|
||||||
}
|
|
||||||
|
|
||||||
percent = dm_make_percent(status->used_sectors, status->total_sectors);
|
|
||||||
if (percent >= state->percent_check) {
|
|
||||||
/* Usage has raised more than CHECK_STEP since the last
|
|
||||||
time. Run actions. */
|
|
||||||
state->percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
|
|
||||||
|
|
||||||
if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
|
|
||||||
log_warn("WARNING: Snapshot %s is now %.2f%% full.",
|
|
||||||
device, dm_percent_to_round_float(percent, 2));
|
|
||||||
|
|
||||||
/* Try to extend the snapshot, in accord with user-set policies */
|
|
||||||
if (!_extend(state->cmd_lvextend))
|
|
||||||
log_error("Failed to extend snapshot %s.", device);
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
dm_pool_free(state->mem, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int register_device(const char *device,
|
|
||||||
const char *uuid __attribute__((unused)),
|
|
||||||
int major __attribute__((unused)),
|
|
||||||
int minor __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state;
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_init_with_pool("snapshot_state", state))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvextend,
|
|
||||||
sizeof(state->cmd_lvextend),
|
|
||||||
"lvextend --use-policies", device))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
state->percent_check = CHECK_MINIMUM;
|
|
||||||
*user = state;
|
|
||||||
|
|
||||||
log_info("Monitoring snapshot %s.", device);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
bad:
|
|
||||||
log_error("Failed to monitor snapshot %s.", device);
|
|
||||||
|
|
||||||
if (state)
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int unregister_device(const char *device,
|
|
||||||
const char *uuid __attribute__((unused)),
|
|
||||||
int major __attribute__((unused)),
|
|
||||||
int minor __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state = *user;
|
|
||||||
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
log_info("No longer monitoring snapshot %s.", device);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
process_event
|
|
||||||
register_device
|
|
||||||
unregister_device
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2011-2014 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
|
|
||||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
|
|
||||||
|
|
||||||
SOURCES = dmeventd_thin.c
|
|
||||||
|
|
||||||
LIB_NAME = libdevmapper-event-lvm2thin
|
|
||||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
|
|
||||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
|
||||||
|
|
||||||
CFLOW_LIST = $(SOURCES)
|
|
||||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
LIBS += -ldevmapper-event-lvm2 -ldevmapper
|
|
||||||
|
|
||||||
install_lvm2: install_dm_plugin
|
|
||||||
|
|
||||||
install: install_lvm2
|
|
||||||
@@ -1,438 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011-2017 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of LVM2.
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU Lesser General Public License v.2.1.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "lib.h" /* using here lvm log */
|
|
||||||
#include "dmeventd_lvm.h"
|
|
||||||
#include "libdevmapper-event.h"
|
|
||||||
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
/* TODO - move this mountinfo code into library to be reusable */
|
|
||||||
#ifdef __linux__
|
|
||||||
# include "kdev_t.h"
|
|
||||||
#else
|
|
||||||
# define MAJOR(x) major((x))
|
|
||||||
# define MINOR(x) minor((x))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* First warning when thin data or metadata is 80% full. */
|
|
||||||
#define WARNING_THRESH (DM_PERCENT_1 * 80)
|
|
||||||
/* Umount thin LVs when thin data or metadata LV is >=
|
|
||||||
* and lvextend --use-policies has failed. */
|
|
||||||
#define UMOUNT_THRESH (DM_PERCENT_1 * 95)
|
|
||||||
/* Run a check every 5%. */
|
|
||||||
#define CHECK_STEP (DM_PERCENT_1 * 5)
|
|
||||||
/* Do not bother checking thin data or metadata is less than 50% full. */
|
|
||||||
#define CHECK_MINIMUM (DM_PERCENT_1 * 50)
|
|
||||||
|
|
||||||
#define UMOUNT_COMMAND "/bin/umount"
|
|
||||||
|
|
||||||
#define MAX_FAILS (256) /* ~42 mins between cmd call retry with 10s delay */
|
|
||||||
|
|
||||||
#define THIN_DEBUG 0
|
|
||||||
|
|
||||||
struct dso_state {
|
|
||||||
struct dm_pool *mem;
|
|
||||||
int metadata_percent_check;
|
|
||||||
int metadata_percent;
|
|
||||||
int data_percent_check;
|
|
||||||
int data_percent;
|
|
||||||
uint64_t known_metadata_size;
|
|
||||||
uint64_t known_data_size;
|
|
||||||
unsigned fails;
|
|
||||||
unsigned max_fails;
|
|
||||||
int restore_sigset;
|
|
||||||
sigset_t old_sigset;
|
|
||||||
pid_t pid;
|
|
||||||
char *argv[3];
|
|
||||||
char *cmd_str;
|
|
||||||
};
|
|
||||||
|
|
||||||
DM_EVENT_LOG_FN("thin")
|
|
||||||
|
|
||||||
#define UUID_PREFIX "LVM-"
|
|
||||||
|
|
||||||
static int _run_command(struct dso_state *state)
|
|
||||||
{
|
|
||||||
char val[3][36];
|
|
||||||
char *env[] = { val[0], val[1], val[2], NULL };
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Mark for possible lvm2 command we are running from dmeventd
|
|
||||||
* lvm2 will not try to talk back to dmeventd while processing it */
|
|
||||||
(void) dm_snprintf(val[0], sizeof(val[0]), "LVM_RUN_BY_DMEVENTD=1");
|
|
||||||
|
|
||||||
if (state->data_percent) {
|
|
||||||
/* Prepare some known data to env vars for easy use */
|
|
||||||
(void) dm_snprintf(val[1], sizeof(val[1]), "DMEVENTD_THIN_POOL_DATA=%d",
|
|
||||||
state->data_percent / DM_PERCENT_1);
|
|
||||||
(void) dm_snprintf(val[2], sizeof(val[2]), "DMEVENTD_THIN_POOL_METADATA=%d",
|
|
||||||
state->metadata_percent / DM_PERCENT_1);
|
|
||||||
} else {
|
|
||||||
/* For an error event it's for a user to check status and decide */
|
|
||||||
env[1] = NULL;
|
|
||||||
log_debug("Error event processing.");
|
|
||||||
}
|
|
||||||
|
|
||||||
log_verbose("Executing command: %s", state->cmd_str);
|
|
||||||
|
|
||||||
/* TODO:
|
|
||||||
* Support parallel run of 'task' and it's waitpid maintainence
|
|
||||||
* ATM we can't handle signaling of SIGALRM
|
|
||||||
* as signalling is not allowed while 'process_event()' is running
|
|
||||||
*/
|
|
||||||
if (!(state->pid = fork())) {
|
|
||||||
/* child */
|
|
||||||
(void) close(0);
|
|
||||||
for (i = 3; i < 255; ++i) (void) close(i);
|
|
||||||
execve(state->argv[0], state->argv, env);
|
|
||||||
_exit(errno);
|
|
||||||
} else if (state->pid == -1) {
|
|
||||||
log_error("Can't fork command %s.", state->cmd_str);
|
|
||||||
state->fails = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
|
|
||||||
{
|
|
||||||
#if THIN_DEBUG
|
|
||||||
log_debug("dmeventd executes: %s.", state->cmd_str);
|
|
||||||
#endif
|
|
||||||
if (state->argv[0])
|
|
||||||
return _run_command(state);
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) {
|
|
||||||
log_error("Failed command for %s.", dm_task_get_name(dmt));
|
|
||||||
state->fails = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->fails = 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if executed command has finished
|
|
||||||
* Only 1 command may run */
|
|
||||||
static int _wait_for_pid(struct dso_state *state)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
if (state->pid == -1)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (!waitpid(state->pid, &status, WNOHANG))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Wait for finish */
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
log_verbose("Child %d exited with status %d.",
|
|
||||||
state->pid, WEXITSTATUS(status));
|
|
||||||
state->fails = WEXITSTATUS(status) ? 1 : 0;
|
|
||||||
} else {
|
|
||||||
if (WIFSIGNALED(status))
|
|
||||||
log_verbose("Child %d was terminated with status %d.",
|
|
||||||
state->pid, WTERMSIG(status));
|
|
||||||
state->fails = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->pid = -1;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_event(struct dm_task *dmt,
|
|
||||||
enum dm_event_mask event __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
const char *device = dm_task_get_name(dmt);
|
|
||||||
struct dso_state *state = *user;
|
|
||||||
struct dm_status_thin_pool *tps = NULL;
|
|
||||||
void *next = NULL;
|
|
||||||
uint64_t start, length;
|
|
||||||
char *target_type = NULL;
|
|
||||||
char *params;
|
|
||||||
int needs_policy = 0;
|
|
||||||
struct dm_task *new_dmt = NULL;
|
|
||||||
|
|
||||||
#if THIN_DEBUG
|
|
||||||
log_debug("Watch for tp-data:%.2f%% tp-metadata:%.2f%%.",
|
|
||||||
dm_percent_to_round_float(state->data_percent_check, 2),
|
|
||||||
dm_percent_to_round_float(state->metadata_percent_check, 2));
|
|
||||||
#endif
|
|
||||||
if (!_wait_for_pid(state)) {
|
|
||||||
log_warn("WARNING: Skipping event, child %d is still running (%s).",
|
|
||||||
state->pid, state->cmd_str);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event & DM_EVENT_DEVICE_ERROR) {
|
|
||||||
/* Error -> no need to check and do instant resize */
|
|
||||||
state->data_percent = state->metadata_percent = 0;
|
|
||||||
if (_use_policy(dmt, state))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
stack;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Rather update oldish status
|
|
||||||
* since after 'command' processing
|
|
||||||
* percentage info could have changed a lot.
|
|
||||||
* If we would get above UMOUNT_THRESH
|
|
||||||
* we would wait for next sigalarm.
|
|
||||||
*/
|
|
||||||
if (!(new_dmt = dm_task_create(DM_DEVICE_STATUS)))
|
|
||||||
goto_out;
|
|
||||||
|
|
||||||
if (!dm_task_set_uuid(new_dmt, dm_task_get_uuid(dmt)))
|
|
||||||
goto_out;
|
|
||||||
|
|
||||||
/* Non-blocking status read */
|
|
||||||
if (!dm_task_no_flush(new_dmt))
|
|
||||||
log_warn("WARNING: Can't set no_flush for dm status.");
|
|
||||||
|
|
||||||
if (!dm_task_run(new_dmt))
|
|
||||||
goto_out;
|
|
||||||
|
|
||||||
dmt = new_dmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
|
||||||
|
|
||||||
if (!target_type || (strcmp(target_type, "thin-pool") != 0)) {
|
|
||||||
log_error("Invalid target type.");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dm_get_status_thin_pool(state->mem, params, &tps)) {
|
|
||||||
log_error("Failed to parse status.");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if THIN_DEBUG
|
|
||||||
log_debug("Thin pool status " FMTu64 "/" FMTu64 " "
|
|
||||||
FMTu64 "/" FMTu64 ".",
|
|
||||||
tps->used_metadata_blocks, tps->total_metadata_blocks,
|
|
||||||
tps->used_data_blocks, tps->total_data_blocks);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Thin pool size had changed. Clear the threshold. */
|
|
||||||
if (state->known_metadata_size != tps->total_metadata_blocks) {
|
|
||||||
state->metadata_percent_check = CHECK_MINIMUM;
|
|
||||||
state->known_metadata_size = tps->total_metadata_blocks;
|
|
||||||
state->fails = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->known_data_size != tps->total_data_blocks) {
|
|
||||||
state->data_percent_check = CHECK_MINIMUM;
|
|
||||||
state->known_data_size = tps->total_data_blocks;
|
|
||||||
state->fails = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Trigger action when threshold boundary is exceeded.
|
|
||||||
* Report 80% threshold warning when it's used above 80%.
|
|
||||||
* Only 100% is exception as it cannot be surpased so policy
|
|
||||||
* action is called for: >50%, >55% ... >95%, 100%
|
|
||||||
*/
|
|
||||||
state->metadata_percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks);
|
|
||||||
if ((state->metadata_percent > WARNING_THRESH) &&
|
|
||||||
(state->metadata_percent > state->metadata_percent_check))
|
|
||||||
log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.",
|
|
||||||
device, dm_percent_to_round_float(state->metadata_percent, 2));
|
|
||||||
if (state->metadata_percent > CHECK_MINIMUM) {
|
|
||||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
|
||||||
if (state->metadata_percent > state->metadata_percent_check)
|
|
||||||
needs_policy = 1;
|
|
||||||
state->metadata_percent_check = (state->metadata_percent / CHECK_STEP + 1) * CHECK_STEP;
|
|
||||||
if (state->metadata_percent_check == DM_PERCENT_100)
|
|
||||||
state->metadata_percent_check--; /* Can't get bigger then 100% */
|
|
||||||
} else
|
|
||||||
state->metadata_percent_check = CHECK_MINIMUM;
|
|
||||||
|
|
||||||
state->data_percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks);
|
|
||||||
if ((state->data_percent > WARNING_THRESH) &&
|
|
||||||
(state->data_percent > state->data_percent_check))
|
|
||||||
log_warn("WARNING: Thin pool %s data is now %.2f%% full.",
|
|
||||||
device, dm_percent_to_round_float(state->data_percent, 2));
|
|
||||||
if (state->data_percent > CHECK_MINIMUM) {
|
|
||||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
|
||||||
if (state->data_percent > state->data_percent_check)
|
|
||||||
needs_policy = 1;
|
|
||||||
state->data_percent_check = (state->data_percent / CHECK_STEP + 1) * CHECK_STEP;
|
|
||||||
if (state->data_percent_check == DM_PERCENT_100)
|
|
||||||
state->data_percent_check--; /* Can't get bigger then 100% */
|
|
||||||
} else
|
|
||||||
state->data_percent_check = CHECK_MINIMUM;
|
|
||||||
|
|
||||||
/* Reduce number of _use_policy() calls by power-of-2 factor till frequency of MAX_FAILS is reached.
|
|
||||||
* Avoids too high number of error retries, yet shows some status messages in log regularly.
|
|
||||||
* i.e. PV could have been pvmoved and VG/LV was locked for a while...
|
|
||||||
*/
|
|
||||||
if (state->fails) {
|
|
||||||
if (state->fails++ <= state->max_fails) {
|
|
||||||
log_debug("Postponing frequently failing policy (%u <= %u).",
|
|
||||||
state->fails - 1, state->max_fails);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state->max_fails < MAX_FAILS)
|
|
||||||
state->max_fails <<= 1;
|
|
||||||
state->fails = needs_policy = 1; /* Retry failing command */
|
|
||||||
} else
|
|
||||||
state->max_fails = 1; /* Reset on success */
|
|
||||||
|
|
||||||
if (needs_policy)
|
|
||||||
_use_policy(dmt, state);
|
|
||||||
out:
|
|
||||||
if (tps)
|
|
||||||
dm_pool_free(state->mem, tps);
|
|
||||||
|
|
||||||
if (new_dmt)
|
|
||||||
dm_task_destroy(new_dmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle SIGCHLD for a thread */
|
|
||||||
static void _sig_child(int signum __attribute__((unused)))
|
|
||||||
{
|
|
||||||
/* empty SIG_IGN */;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup handler for SIGCHLD when executing external command
|
|
||||||
* to get quick 'waitpid()' reaction
|
|
||||||
* It will interrupt syscall just like SIGALRM and
|
|
||||||
* invoke process_event().
|
|
||||||
*/
|
|
||||||
static void _init_thread_signals(struct dso_state *state)
|
|
||||||
{
|
|
||||||
struct sigaction act = { .sa_handler = _sig_child };
|
|
||||||
sigset_t my_sigset;
|
|
||||||
|
|
||||||
sigemptyset(&my_sigset);
|
|
||||||
|
|
||||||
if (sigaction(SIGCHLD, &act, NULL))
|
|
||||||
log_warn("WARNING: Failed to set SIGCHLD action.");
|
|
||||||
else if (sigaddset(&my_sigset, SIGCHLD))
|
|
||||||
log_warn("WARNING: Failed to add SIGCHLD to set.");
|
|
||||||
else if (pthread_sigmask(SIG_UNBLOCK, &my_sigset, &state->old_sigset))
|
|
||||||
log_warn("WARNING: Failed to unblock SIGCHLD.");
|
|
||||||
else
|
|
||||||
state->restore_sigset = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _restore_thread_signals(struct dso_state *state)
|
|
||||||
{
|
|
||||||
if (state->restore_sigset &&
|
|
||||||
pthread_sigmask(SIG_SETMASK, &state->old_sigset, NULL))
|
|
||||||
log_warn("WARNING: Failed to block SIGCHLD.");
|
|
||||||
}
|
|
||||||
|
|
||||||
int register_device(const char *device,
|
|
||||||
const char *uuid __attribute__((unused)),
|
|
||||||
int major __attribute__((unused)),
|
|
||||||
int minor __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state;
|
|
||||||
char *str;
|
|
||||||
char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_init_with_pool("thin_pool_state", state))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str),
|
|
||||||
"_dmeventd_thin_command", device))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
if (strncmp(cmd_str, "lvm ", 4) == 0) {
|
|
||||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) {
|
|
||||||
log_error("Failed to copy lvm command.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
} else if (cmd_str[0] == '/') {
|
|
||||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) {
|
|
||||||
log_error("Failed to copy thin command.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find last space before 'vg/lv' */
|
|
||||||
if (!(str = strrchr(state->cmd_str, ' ')))
|
|
||||||
goto inval;
|
|
||||||
|
|
||||||
if (!(state->argv[0] = dm_pool_strndup(state->mem, state->cmd_str,
|
|
||||||
str - state->cmd_str))) {
|
|
||||||
log_error("Failed to copy command.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->argv[1] = str + 1; /* 1 argument - vg/lv */
|
|
||||||
_init_thread_signals(state);
|
|
||||||
} else /* Unuspported command format */
|
|
||||||
goto inval;
|
|
||||||
|
|
||||||
state->pid = -1;
|
|
||||||
*user = state;
|
|
||||||
|
|
||||||
log_info("Monitoring thin pool %s.", device);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
inval:
|
|
||||||
log_error("Invalid command for monitoring: %s.", cmd_str);
|
|
||||||
bad:
|
|
||||||
log_error("Failed to monitor thin pool %s.", device);
|
|
||||||
|
|
||||||
if (state)
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int unregister_device(const char *device,
|
|
||||||
const char *uuid __attribute__((unused)),
|
|
||||||
int major __attribute__((unused)),
|
|
||||||
int minor __attribute__((unused)),
|
|
||||||
void **user)
|
|
||||||
{
|
|
||||||
struct dso_state *state = *user;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; !_wait_for_pid(state) && (i < 6); ++i) {
|
|
||||||
if (i == 0)
|
|
||||||
/* Give it 2 seconds, then try to terminate & kill it */
|
|
||||||
log_verbose("Child %d still not finished (%s) waiting.",
|
|
||||||
state->pid, state->cmd_str);
|
|
||||||
else if (i == 3) {
|
|
||||||
log_warn("WARNING: Terminating child %d.", state->pid);
|
|
||||||
kill(state->pid, SIGINT);
|
|
||||||
kill(state->pid, SIGTERM);
|
|
||||||
} else if (i == 5) {
|
|
||||||
log_warn("WARNING: Killing child %d.", state->pid);
|
|
||||||
kill(state->pid, SIGKILL);
|
|
||||||
}
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->pid != -1)
|
|
||||||
log_warn("WARNING: Cannot kill child %d!", state->pid);
|
|
||||||
|
|
||||||
_restore_thread_signals(state);
|
|
||||||
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
log_info("No longer monitoring thin pool %s.", device);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
1
daemons/dmfilemapd/.gitignore
vendored
1
daemons/dmfilemapd/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
dmfilemapd
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of the device-mapper userspace tools.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU Lesser General Public License v.2.1.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
SOURCES = dmfilemapd.c
|
|
||||||
|
|
||||||
TARGETS = dmfilemapd
|
|
||||||
|
|
||||||
.PHONY: install_dmfilemapd install_dmfilemapd_static
|
|
||||||
|
|
||||||
INSTALL_DMFILEMAPD_TARGETS = install_dmfilemapd_dynamic
|
|
||||||
|
|
||||||
CLEAN_TARGETS = dmfilemapd.static
|
|
||||||
|
|
||||||
CFLOW_LIST = $(SOURCES)
|
|
||||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
|
||||||
CFLOW_TARGET = dmfilemapd
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
all: device-mapper
|
|
||||||
device-mapper: $(TARGETS)
|
|
||||||
|
|
||||||
CFLAGS_dmfilemapd.o += $(EXTRA_EXEC_CFLAGS)
|
|
||||||
LIBS += -ldevmapper
|
|
||||||
|
|
||||||
dmfilemapd: $(LIB_SHARED) dmfilemapd.o
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
|
|
||||||
-o $@ dmfilemapd.o $(DL_LIBS) $(LIBS)
|
|
||||||
|
|
||||||
dmfilemapd.static: $(LIB_STATIC) dmfilemapd.o $(interfacebuilddir)/libdevmapper.a
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L$(interfacebuilddir) \
|
|
||||||
-o $@ dmfilemapd.o $(DL_LIBS) $(LIBS) $(STATIC_LIBS)
|
|
||||||
|
|
||||||
ifneq ("$(CFLOW_CMD)", "")
|
|
||||||
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
|
|
||||||
-include $(top_builddir)/libdm/libdevmapper.cflow
|
|
||||||
-include $(top_builddir)/lib/liblvm-internal.cflow
|
|
||||||
-include $(top_builddir)/lib/liblvm2cmd.cflow
|
|
||||||
-include $(top_builddir)/daemons/dmfilemapd/$(LIB_NAME).cflow
|
|
||||||
endif
|
|
||||||
|
|
||||||
install_dmfilemapd_dynamic: dmfilemapd
|
|
||||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
|
||||||
|
|
||||||
install_dmfilemapd_static: dmfilemapd.static
|
|
||||||
$(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
|
|
||||||
|
|
||||||
install_dmfilemapd: $(INSTALL_DMFILEMAPD_TARGETS)
|
|
||||||
|
|
||||||
install: install_dmfilemapd
|
|
||||||
|
|
||||||
install_device-mapper: install_dmfilemapd
|
|
||||||
@@ -1,834 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of the device-mapper userspace tools.
|
|
||||||
*
|
|
||||||
* It includes tree drawing code based on pstree: http://psmisc.sourceforge.net/
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "tool.h"
|
|
||||||
|
|
||||||
#include "dm-logging.h"
|
|
||||||
|
|
||||||
#include "defaults.h"
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/inotify.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
# include "kdev_t.h"
|
|
||||||
#else
|
|
||||||
# define MAJOR(x) major((x))
|
|
||||||
# define MINOR(x) minor((x))
|
|
||||||
# define MKDEV(x,y) makedev((x),(y))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* limit to two updates/sec */
|
|
||||||
#define FILEMAPD_WAIT_USECS 500000
|
|
||||||
|
|
||||||
/* how long to wait for unlinked files */
|
|
||||||
#define FILEMAPD_NOFILE_WAIT_USECS 100000
|
|
||||||
#define FILEMAPD_NOFILE_WAIT_TRIES 10
|
|
||||||
|
|
||||||
struct filemap_monitor {
|
|
||||||
dm_filemapd_mode_t mode;
|
|
||||||
const char *program_id;
|
|
||||||
uint64_t group_id;
|
|
||||||
char *path;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
int inotify_fd;
|
|
||||||
int inotify_watch_fd;
|
|
||||||
|
|
||||||
/* monitoring heuristics */
|
|
||||||
int64_t blocks; /* allocated blocks, from stat.st_blocks */
|
|
||||||
uint64_t nr_regions;
|
|
||||||
int deleted;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int _foreground;
|
|
||||||
static int _verbose;
|
|
||||||
|
|
||||||
const char *const _usage = "dmfilemapd <fd> <group_id> <abs_path> <mode> "
|
|
||||||
"[<foreground>[<log_level>]]";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Daemon logging. By default, all messages are thrown away: messages
|
|
||||||
* are only written to the terminal if the daemon is run in the foreground.
|
|
||||||
*/
|
|
||||||
__attribute__((format(printf, 5, 0)))
|
|
||||||
static void _dmfilemapd_log_line(int level,
|
|
||||||
const char *file __attribute__((unused)),
|
|
||||||
int line __attribute__((unused)),
|
|
||||||
int dm_errno_or_class,
|
|
||||||
const char *f, va_list ap)
|
|
||||||
{
|
|
||||||
static int _abort_on_internal_errors = -1;
|
|
||||||
FILE *out = log_stderr(level) ? stderr : stdout;
|
|
||||||
|
|
||||||
level = log_level(level);
|
|
||||||
|
|
||||||
if (level <= _LOG_WARN || _verbose) {
|
|
||||||
if (level < _LOG_WARN)
|
|
||||||
out = stderr;
|
|
||||||
vfprintf(out, f, ap);
|
|
||||||
fputc('\n', out);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_abort_on_internal_errors < 0)
|
|
||||||
/* Set when env DM_ABORT_ON_INTERNAL_ERRORS is not "0" */
|
|
||||||
_abort_on_internal_errors =
|
|
||||||
strcmp(getenv("DM_ABORT_ON_INTERNAL_ERRORS") ? : "0", "0");
|
|
||||||
|
|
||||||
if (_abort_on_internal_errors &&
|
|
||||||
!strncmp(f, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1))
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((format(printf, 5, 6)))
|
|
||||||
static void _dmfilemapd_log_with_errno(int level,
|
|
||||||
const char *file, int line,
|
|
||||||
int dm_errno_or_class,
|
|
||||||
const char *f, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, f);
|
|
||||||
_dmfilemapd_log_line(level, file, line, dm_errno_or_class, f, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only used for reporting errors before daemonise().
|
|
||||||
*/
|
|
||||||
__attribute__((format(printf, 1, 2)))
|
|
||||||
static void _early_log(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
fputc('\n', stderr);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _setup_logging(void)
|
|
||||||
{
|
|
||||||
dm_log_init_verbose(_verbose - 1);
|
|
||||||
dm_log_with_errno_init(_dmfilemapd_log_with_errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PROC_FD_DELETED_STR "(deleted)"
|
|
||||||
/*
|
|
||||||
* Scan the /proc/<pid>/fd directory for pid and check for an fd
|
|
||||||
* symlink whose contents match path.
|
|
||||||
*/
|
|
||||||
static int _is_open_in_pid(pid_t pid, const char *path)
|
|
||||||
{
|
|
||||||
char deleted_path[PATH_MAX + sizeof(PROC_FD_DELETED_STR)];
|
|
||||||
struct dirent *pid_dp = NULL;
|
|
||||||
char path_buf[PATH_MAX];
|
|
||||||
char link_buf[PATH_MAX];
|
|
||||||
DIR *pid_d = NULL;
|
|
||||||
ssize_t len;
|
|
||||||
|
|
||||||
if (pid == getpid())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (dm_snprintf(path_buf, sizeof(path_buf),
|
|
||||||
DEFAULT_PROC_DIR "%d/fd", pid) < 0) {
|
|
||||||
log_error("Could not format pid path.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test for the kernel 'file (deleted)' form when scanning.
|
|
||||||
*/
|
|
||||||
if (dm_snprintf(deleted_path, sizeof(deleted_path), "%s %s",
|
|
||||||
path, PROC_FD_DELETED_STR) < 0) {
|
|
||||||
log_error("Could not format check path.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_d = opendir(path_buf);
|
|
||||||
if (!pid_d) {
|
|
||||||
log_error("Could not open proc path: %s.", path_buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((pid_dp = readdir(pid_d)) != NULL) {
|
|
||||||
if (pid_dp->d_name[0] == '.')
|
|
||||||
continue;
|
|
||||||
if ((len = readlinkat(dirfd(pid_d), pid_dp->d_name, link_buf,
|
|
||||||
sizeof(link_buf))) < 0) {
|
|
||||||
log_error("readlink failed for " DEFAULT_PROC_DIR
|
|
||||||
"/%d/fd/.", pid);
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
link_buf[len] = '\0';
|
|
||||||
if (!strcmp(deleted_path, link_buf)) {
|
|
||||||
if (closedir(pid_d))
|
|
||||||
log_sys_error("closedir", path_buf);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bad:
|
|
||||||
if (closedir(pid_d))
|
|
||||||
log_sys_error("closedir", path_buf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempt to determine whether a file is open by any process by
|
|
||||||
* scanning symbolic links in /proc/<pid>/fd.
|
|
||||||
*
|
|
||||||
* This is a heuristic since it cannot guarantee to detect brief
|
|
||||||
* access in all cases: a process that opens and then closes the
|
|
||||||
* file rapidly may never be seen by the scan.
|
|
||||||
*
|
|
||||||
* The method will also give false-positives if a process exists
|
|
||||||
* that has a deleted file open that had the same path, but a
|
|
||||||
* different inode number, to the file being monitored.
|
|
||||||
*
|
|
||||||
* For this reason the daemon only uses _is_open() for unlinked
|
|
||||||
* files when the mode is DM_FILEMAPD_FOLLOW_INODE, since these
|
|
||||||
* files can no longer be newly opened by processes.
|
|
||||||
*
|
|
||||||
* In this situation !is_open(path) provides an indication that
|
|
||||||
* the daemon should shut down: the file has been unlinked from
|
|
||||||
* the file system and we appear to hold the final reference.
|
|
||||||
*/
|
|
||||||
static int _is_open(const char *path)
|
|
||||||
{
|
|
||||||
struct dirent *proc_dp = NULL;
|
|
||||||
DIR *proc_d = NULL;
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
proc_d = opendir(DEFAULT_PROC_DIR);
|
|
||||||
if (!proc_d)
|
|
||||||
return 0;
|
|
||||||
while ((proc_dp = readdir(proc_d)) != NULL) {
|
|
||||||
if (!isdigit(proc_dp->d_name[0]))
|
|
||||||
continue;
|
|
||||||
errno = 0;
|
|
||||||
pid = (pid_t) strtol(proc_dp->d_name, NULL, 10);
|
|
||||||
if (errno || !pid)
|
|
||||||
continue;
|
|
||||||
if (_is_open_in_pid(pid, path)) {
|
|
||||||
if (closedir(proc_d))
|
|
||||||
log_sys_error("closedir", DEFAULT_PROC_DIR);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closedir(proc_d))
|
|
||||||
log_sys_error("closedir", DEFAULT_PROC_DIR);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _filemap_monitor_wait(uint64_t usecs)
|
|
||||||
{
|
|
||||||
if (_verbose) {
|
|
||||||
if (usecs == FILEMAPD_WAIT_USECS)
|
|
||||||
log_very_verbose("Waiting for check interval");
|
|
||||||
if (usecs == FILEMAPD_NOFILE_WAIT_USECS)
|
|
||||||
log_very_verbose("Waiting for unlinked path");
|
|
||||||
}
|
|
||||||
usleep((useconds_t) usecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _parse_args(int argc, char **argv, struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
char *endptr;
|
|
||||||
|
|
||||||
/* we don't care what is in argv[0]. */
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
if (argc < 5) {
|
|
||||||
_early_log("Wrong number of arguments.");
|
|
||||||
_early_log("usage: %s", _usage);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We don't know the true nr_regions at daemon start time,
|
|
||||||
* and it is not worth a dm_stats_list()/group walk to count:
|
|
||||||
* we can assume that there is at least one region or the
|
|
||||||
* daemon would not have been started.
|
|
||||||
*
|
|
||||||
* A correct value will be obtained following the first update
|
|
||||||
* of the group's regions.
|
|
||||||
*/
|
|
||||||
fm->nr_regions = 1;
|
|
||||||
|
|
||||||
/* parse <fd> */
|
|
||||||
errno = 0;
|
|
||||||
fm->fd = (int) strtol(argv[0], &endptr, 10);
|
|
||||||
if (errno || *endptr) {
|
|
||||||
_early_log("Could not parse file descriptor: %s", argv[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
/* parse <group_id> */
|
|
||||||
errno = 0;
|
|
||||||
fm->group_id = strtoull(argv[0], &endptr, 10);
|
|
||||||
if (*endptr || errno) {
|
|
||||||
_early_log("Could not parse group identifier: %s", argv[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
/* parse <path> */
|
|
||||||
if (!argv[0] || !strlen(argv[0])) {
|
|
||||||
_early_log("Path argument is required.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*argv[0] != '/') {
|
|
||||||
_early_log("Path argument must specify an absolute path.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fm->path = dm_strdup(argv[0]);
|
|
||||||
if (!fm->path) {
|
|
||||||
_early_log("Could not allocate memory for path argument.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
/* parse <mode> */
|
|
||||||
if (!argv[0] || !strlen(argv[0])) {
|
|
||||||
_early_log("Mode argument is required.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fm->mode = dm_filemapd_mode_from_string(argv[0]);
|
|
||||||
if (fm->mode == DM_FILEMAPD_FOLLOW_NONE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
/* parse [<foreground>[<verbose>]] */
|
|
||||||
if (argc) {
|
|
||||||
errno = 0;
|
|
||||||
_foreground = (int) strtol(argv[0], &endptr, 10);
|
|
||||||
if (errno || *endptr) {
|
|
||||||
_early_log("Could not parse debug argument: %s.",
|
|
||||||
argv[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
if (argc) {
|
|
||||||
errno = 0;
|
|
||||||
_verbose = (int) strtol(argv[0], &endptr, 10);
|
|
||||||
if (errno || *endptr) {
|
|
||||||
_early_log("Could not parse verbose "
|
|
||||||
"argument: %s", argv[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (_verbose < 0 || _verbose > 3) {
|
|
||||||
_early_log("Verbose argument out of range: %d.",
|
|
||||||
_verbose);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_fd_update_blocks(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
struct stat buf;
|
|
||||||
|
|
||||||
if (fm->fd < 0) {
|
|
||||||
log_error("Filemap fd is not open.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fstat(fm->fd, &buf)) {
|
|
||||||
log_error("Failed to fstat filemap file descriptor.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fm->blocks = buf.st_blocks;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_fd_check_changed(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
int64_t old_blocks;
|
|
||||||
|
|
||||||
old_blocks = fm->blocks;
|
|
||||||
|
|
||||||
if (!_filemap_fd_update_blocks(fm))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return (fm->blocks != old_blocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _filemap_monitor_close_fd(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
if (close(fm->fd))
|
|
||||||
log_error("Error closing file descriptor.");
|
|
||||||
fm->fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _filemap_monitor_end_notify(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
inotify_rm_watch(fm->inotify_fd, fm->inotify_watch_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_set_notify(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
int inotify_fd, watch_fd;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set IN_NONBLOCK since we do not want to block in event read()
|
|
||||||
* calls. Do not set IN_CLOEXEC as dmfilemapd is single-threaded
|
|
||||||
* and does not fork or exec.
|
|
||||||
*/
|
|
||||||
if ((inotify_fd = inotify_init1(IN_NONBLOCK)) < 0) {
|
|
||||||
log_sys_error("inotify_init1", "IN_NONBLOCK");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((watch_fd = inotify_add_watch(inotify_fd, fm->path,
|
|
||||||
IN_MODIFY | IN_DELETE_SELF)) < 0) {
|
|
||||||
log_sys_error("inotify_add_watch", fm->path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fm->inotify_fd = inotify_fd;
|
|
||||||
fm->inotify_watch_fd = watch_fd;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_reopen_fd(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
int tries = FILEMAPD_NOFILE_WAIT_TRIES;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In DM_FILEMAPD_FOLLOW_PATH mode, inotify watches must be
|
|
||||||
* re-established whenever the file at the watched path is
|
|
||||||
* changed.
|
|
||||||
*
|
|
||||||
* FIXME: stat file and skip if inode is unchanged.
|
|
||||||
*/
|
|
||||||
if (fm->fd > 0)
|
|
||||||
log_error("Filemap file descriptor already open.");
|
|
||||||
|
|
||||||
while ((fm->fd < 0) && --tries)
|
|
||||||
if (((fm->fd = open(fm->path, O_RDONLY)) < 0) && tries)
|
|
||||||
_filemap_monitor_wait(FILEMAPD_NOFILE_WAIT_USECS);
|
|
||||||
|
|
||||||
if (!tries && (fm->fd < 0)) {
|
|
||||||
log_error("Could not re-open file descriptor.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _filemap_monitor_set_notify(fm);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_get_events(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
/* alignment as per man(7) inotify */
|
|
||||||
char buf[sizeof(struct inotify_event) + NAME_MAX + 1]
|
|
||||||
__attribute__ ((aligned(__alignof__(struct inotify_event))));
|
|
||||||
|
|
||||||
struct inotify_event *event;
|
|
||||||
int check = 0;
|
|
||||||
ssize_t len;
|
|
||||||
char *ptr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Close the file descriptor for the file being monitored here
|
|
||||||
* when mode=path: this will allow the inode to be de-allocated,
|
|
||||||
* and an IN_DELETE_SELF event generated in the case that the
|
|
||||||
* daemon is holding the last open reference to the file.
|
|
||||||
*/
|
|
||||||
if (fm->mode == DM_FILEMAPD_FOLLOW_PATH) {
|
|
||||||
_filemap_monitor_end_notify(fm);
|
|
||||||
_filemap_monitor_close_fd(fm);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = read(fm->inotify_fd, (void *) &buf, sizeof(buf));
|
|
||||||
|
|
||||||
/* no events to read? */
|
|
||||||
if (len < 0 && (errno == EAGAIN))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* interrupted by signal? */
|
|
||||||
if (len < 0 && (errno == EINTR))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (len < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (!len)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
for (ptr = buf; ptr < buf + len; ptr += sizeof(*event) + event->len) {
|
|
||||||
event = (struct inotify_event *) ptr;
|
|
||||||
if (event->mask & IN_DELETE_SELF)
|
|
||||||
fm->deleted = 1;
|
|
||||||
if (event->mask & IN_MODIFY)
|
|
||||||
check = 1;
|
|
||||||
/*
|
|
||||||
* Event IN_IGNORED is generated when a file has been deleted
|
|
||||||
* and IN_DELETE_SELF generated, and indicates that the file
|
|
||||||
* watch has been automatically removed.
|
|
||||||
*
|
|
||||||
* This can only happen for the DM_FILEMAPD_FOLLOW_PATH mode,
|
|
||||||
* since inotify IN_DELETE events are generated at the time
|
|
||||||
* the inode is destroyed: DM_FILEMAPD_FOLLOW_INODE will hold
|
|
||||||
* the file descriptor open, meaning that the event will not
|
|
||||||
* be generated until after the daemon closes the file.
|
|
||||||
*
|
|
||||||
* The event is ignored here since inotify monitoring will
|
|
||||||
* be reestablished (or the daemon will terminate) following
|
|
||||||
* deletion of a DM_FILEMAPD_FOLLOW_PATH monitored file.
|
|
||||||
*/
|
|
||||||
if (event->mask & IN_IGNORED)
|
|
||||||
log_very_verbose("Inotify watch removed: IN_IGNORED "
|
|
||||||
"in event->mask");
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
/*
|
|
||||||
* Re-open file descriptor if required and log disposition.
|
|
||||||
*/
|
|
||||||
if (fm->mode == DM_FILEMAPD_FOLLOW_PATH)
|
|
||||||
if (!_filemap_monitor_reopen_fd(fm))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
log_very_verbose("exiting _filemap_monitor_get_events() with "
|
|
||||||
"deleted=%d, check=%d", fm->deleted, check);
|
|
||||||
return check;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _filemap_monitor_destroy(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
if (fm->fd > 0) {
|
|
||||||
_filemap_monitor_end_notify(fm);
|
|
||||||
_filemap_monitor_close_fd(fm);
|
|
||||||
}
|
|
||||||
dm_free((void *) fm->program_id);
|
|
||||||
dm_free(fm->path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_check_same_file(int fd1, int fd2)
|
|
||||||
{
|
|
||||||
struct stat buf1, buf2;
|
|
||||||
|
|
||||||
if ((fd1 < 0) || (fd2 < 0))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (fstat(fd1, &buf1)) {
|
|
||||||
log_error("Failed to fstat file descriptor %d", fd1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fstat(fd2, &buf2)) {
|
|
||||||
log_error("Failed to fstat file descriptor %d", fd2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ((buf1.st_dev == buf2.st_dev) && (buf1.st_ino == buf2.st_ino));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_check_file_unlinked(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
char path_buf[PATH_MAX];
|
|
||||||
char link_buf[PATH_MAX];
|
|
||||||
int same, fd;
|
|
||||||
ssize_t len;
|
|
||||||
|
|
||||||
fm->deleted = 0;
|
|
||||||
same = 0;
|
|
||||||
|
|
||||||
if ((fd = open(fm->path, O_RDONLY)) < 0)
|
|
||||||
goto check_unlinked;
|
|
||||||
|
|
||||||
same = _filemap_monitor_check_same_file(fm->fd, fd);
|
|
||||||
|
|
||||||
if (close(fd))
|
|
||||||
log_error("Error closing fd %d", fd);
|
|
||||||
|
|
||||||
if (same < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (same)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
check_unlinked:
|
|
||||||
/*
|
|
||||||
* The file has been unlinked from its original location: test
|
|
||||||
* whether it is still reachable in the filesystem, or if it is
|
|
||||||
* unlinked and anonymous.
|
|
||||||
*/
|
|
||||||
if (dm_snprintf(path_buf, sizeof(path_buf), DEFAULT_PROC_DIR
|
|
||||||
"/%d/fd/%d", getpid(), fm->fd) < 0) {
|
|
||||||
log_error("Could not format pid path.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if ((len = readlink(path_buf, link_buf, sizeof(link_buf) - 1)) < 0) {
|
|
||||||
log_error("readlink failed for " DEFAULT_PROC_DIR "/%d/fd/%d.",
|
|
||||||
getpid(), fm->fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
link_buf[len] = '\0';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to re-open the file, from the path now reported in /proc/pid/fd.
|
|
||||||
*/
|
|
||||||
if ((fd = open(link_buf, O_RDONLY)) < 0)
|
|
||||||
fm->deleted = 1;
|
|
||||||
else
|
|
||||||
same = _filemap_monitor_check_same_file(fm->fd, fd);
|
|
||||||
|
|
||||||
if ((fd >= 0) && close(fd))
|
|
||||||
log_error("Error closing fd %d", fd);
|
|
||||||
|
|
||||||
if (same < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Should not happen with normal /proc. */
|
|
||||||
if ((fd > 0) && !same) {
|
|
||||||
log_error("File descriptor mismatch: %d and %s (read from %s) "
|
|
||||||
"are not the same file!", fm->fd, link_buf, path_buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _daemonise(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
pid_t pid = 0, sid;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
if (!(sid = setsid())) {
|
|
||||||
_early_log("setsid failed.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((pid = fork()) < 0) {
|
|
||||||
_early_log("Failed to fork daemon process.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid > 0) {
|
|
||||||
if (_verbose)
|
|
||||||
_early_log("Started dmfilemapd with pid=%d", pid);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chdir("/")) {
|
|
||||||
_early_log("Failed to change directory.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_verbose) {
|
|
||||||
if (close(STDIN_FILENO))
|
|
||||||
_early_log("Error closing stdin");
|
|
||||||
if (close(STDOUT_FILENO))
|
|
||||||
_early_log("Error closing stdout");
|
|
||||||
if (close(STDERR_FILENO))
|
|
||||||
_early_log("Error closing stderr");
|
|
||||||
if ((open("/dev/null", O_RDONLY) < 0) ||
|
|
||||||
(open("/dev/null", O_WRONLY) < 0) ||
|
|
||||||
(open("/dev/null", O_WRONLY) < 0)) {
|
|
||||||
_early_log("Error opening stdio streams.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* TODO: Use libdaemon/server/daemon-server.c _daemonise() */
|
|
||||||
for (fd = (int) sysconf(_SC_OPEN_MAX) - 1; fd > STDERR_FILENO; fd--) {
|
|
||||||
if (fd == fm->fd)
|
|
||||||
continue;
|
|
||||||
(void) close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _update_regions(struct dm_stats *dms, struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
uint64_t *regions = NULL, *region, nr_regions = 0;
|
|
||||||
|
|
||||||
regions = dm_stats_update_regions_from_fd(dms, fm->fd, fm->group_id);
|
|
||||||
if (!regions) {
|
|
||||||
log_error("Failed to update filemap regions for group_id="
|
|
||||||
FMTu64 ".", fm->group_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
|
|
||||||
nr_regions++;
|
|
||||||
|
|
||||||
if (!nr_regions)
|
|
||||||
log_warn("File contains no extents: exiting.");
|
|
||||||
|
|
||||||
if (nr_regions && (regions[0] != fm->group_id)) {
|
|
||||||
log_warn("group_id changed from " FMTu64 " to " FMTu64,
|
|
||||||
fm->group_id, regions[0]);
|
|
||||||
fm->group_id = regions[0];
|
|
||||||
}
|
|
||||||
dm_free(regions);
|
|
||||||
fm->nr_regions = nr_regions;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _dmfilemapd(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
int running = 1, check = 0, open = 0;
|
|
||||||
const char *program_id;
|
|
||||||
struct dm_stats *dms;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The correct program_id is retrieved from the group leader
|
|
||||||
* following the call to dm_stats_list().
|
|
||||||
*/
|
|
||||||
if (!(dms = dm_stats_create(NULL)))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
if (!dm_stats_bind_from_fd(dms, fm->fd)) {
|
|
||||||
log_error("Could not bind dm_stats handle to file descriptor "
|
|
||||||
"%d", fm->fd);
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_filemap_monitor_set_notify(fm))
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
if (!_filemap_fd_update_blocks(fm))
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
if (!dm_stats_list(dms, DM_STATS_ALL_PROGRAMS)) {
|
|
||||||
log_error("Failed to list stats handle.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Take the program_id for new regions (created by calls to
|
|
||||||
* dm_stats_update_regions_from_fd()) from the value used by
|
|
||||||
* the group leader.
|
|
||||||
*/
|
|
||||||
program_id = dm_stats_get_region_program_id(dms, fm->group_id);
|
|
||||||
if (program_id)
|
|
||||||
fm->program_id = dm_strdup(program_id);
|
|
||||||
else
|
|
||||||
fm->program_id = NULL;
|
|
||||||
dm_stats_set_program_id(dms, 1, program_id);
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (!dm_stats_group_present(dms, fm->group_id)) {
|
|
||||||
log_info("Filemap group removed: exiting.");
|
|
||||||
running = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((check = _filemap_monitor_get_events(fm)) < 0)
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
if (!check)
|
|
||||||
goto wait;
|
|
||||||
|
|
||||||
if ((check = _filemap_fd_check_changed(fm)) < 0)
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
if (check && !_update_regions(dms, fm))
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
running = !!fm->nr_regions;
|
|
||||||
if (!running)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
wait:
|
|
||||||
_filemap_monitor_wait(FILEMAPD_WAIT_USECS);
|
|
||||||
|
|
||||||
/* mode=inode termination condions */
|
|
||||||
if (fm->mode == DM_FILEMAPD_FOLLOW_INODE) {
|
|
||||||
if (!_filemap_monitor_check_file_unlinked(fm))
|
|
||||||
goto bad;
|
|
||||||
if (fm->deleted && !(open = _is_open(fm->path))) {
|
|
||||||
log_info("File unlinked and closed: exiting.");
|
|
||||||
running = 0;
|
|
||||||
} else if (fm->deleted && open)
|
|
||||||
log_verbose("File unlinked and open: "
|
|
||||||
"continuing.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dm_stats_list(dms, NULL)) {
|
|
||||||
log_error("Failed to list stats handle.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (running);
|
|
||||||
|
|
||||||
_filemap_monitor_destroy(fm);
|
|
||||||
dm_stats_destroy(dms);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
bad:
|
|
||||||
_filemap_monitor_destroy(fm);
|
|
||||||
dm_stats_destroy(dms);
|
|
||||||
log_error("Exiting");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * _mode_names[] = {
|
|
||||||
"inode",
|
|
||||||
"path"
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* dmfilemapd <fd> <group_id> <path> <mode> [<foreground>[<log_level>]]
|
|
||||||
*/
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
struct filemap_monitor fm;
|
|
||||||
|
|
||||||
memset(&fm, 0, sizeof(fm));
|
|
||||||
|
|
||||||
if (!_parse_args(argc, argv, &fm)) {
|
|
||||||
dm_free(fm.path);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setup_logging();
|
|
||||||
|
|
||||||
log_info("Starting dmfilemapd with fd=%d, group_id=" FMTu64 " "
|
|
||||||
"mode=%s, path=%s", fm.fd, fm.group_id,
|
|
||||||
_mode_names[fm.mode], fm.path);
|
|
||||||
|
|
||||||
if (!_foreground && !_daemonise(&fm))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return _dmfilemapd(&fm);
|
|
||||||
}
|
|
||||||
1
daemons/lvmdbusd/.gitignore
vendored
1
daemons/lvmdbusd/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
path.py
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of LVM2.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
lvmdbusdir = $(python3dir)/lvmdbusd
|
|
||||||
|
|
||||||
LVMDBUS_SRCDIR_FILES = \
|
|
||||||
automatedproperties.py \
|
|
||||||
background.py \
|
|
||||||
cfg.py \
|
|
||||||
cmdhandler.py \
|
|
||||||
fetch.py \
|
|
||||||
__init__.py \
|
|
||||||
job.py \
|
|
||||||
loader.py \
|
|
||||||
lvmdb.py \
|
|
||||||
main.py \
|
|
||||||
lvm_shell_proxy.py \
|
|
||||||
lv.py \
|
|
||||||
manager.py \
|
|
||||||
objectmanager.py \
|
|
||||||
pv.py \
|
|
||||||
request.py \
|
|
||||||
state.py \
|
|
||||||
udevwatch.py \
|
|
||||||
utils.py \
|
|
||||||
vg.py
|
|
||||||
|
|
||||||
LVMDBUS_BUILDDIR_FILES = \
|
|
||||||
path.py
|
|
||||||
|
|
||||||
LVMDBUSD = $(srcdir)/lvmdbusd
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
.PHONY: install_lvmdbusd
|
|
||||||
|
|
||||||
install_lvmdbusd:
|
|
||||||
$(INSTALL_DIR) $(sbindir)
|
|
||||||
$(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir)
|
|
||||||
$(INSTALL_DIR) $(DESTDIR)$(lvmdbusdir)
|
|
||||||
(cd $(srcdir); $(INSTALL_DATA) $(LVMDBUS_SRCDIR_FILES) $(DESTDIR)$(lvmdbusdir))
|
|
||||||
$(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(DESTDIR)$(lvmdbusdir)
|
|
||||||
PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbusdir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES)
|
|
||||||
$(CHMOD) 755 $(DESTDIR)$(lvmdbusdir)/__pycache__
|
|
||||||
$(CHMOD) 444 $(DESTDIR)$(lvmdbusdir)/__pycache__/*.py[co]
|
|
||||||
|
|
||||||
install_lvm2: install_lvmdbusd
|
|
||||||
|
|
||||||
install: install_lvm2
|
|
||||||
|
|
||||||
DISTCLEAN_TARGETS+= \
|
|
||||||
$(LVMDBUS_BUILDDIR_FILES)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from .main import main
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import dbus
|
|
||||||
import dbus.service
|
|
||||||
from . import cfg
|
|
||||||
from .utils import get_properties, add_properties, get_object_property_diff, \
|
|
||||||
log_debug
|
|
||||||
from .state import State
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyPep8Naming,PyUnresolvedReferences
|
|
||||||
class AutomatedProperties(dbus.service.Object):
|
|
||||||
"""
|
|
||||||
This class implements the needed interfaces for:
|
|
||||||
org.freedesktop.DBus.Properties
|
|
||||||
|
|
||||||
Other classes inherit from it to get the same behavior
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, object_path, search_method=None):
|
|
||||||
dbus.service.Object.__init__(self, cfg.bus, object_path)
|
|
||||||
self._ap_interface = []
|
|
||||||
self._ap_o_path = object_path
|
|
||||||
self._ap_search_method = search_method
|
|
||||||
self.state = None
|
|
||||||
|
|
||||||
def dbus_object_path(self):
|
|
||||||
return self._ap_o_path
|
|
||||||
|
|
||||||
def emit_data(self):
|
|
||||||
props = {}
|
|
||||||
|
|
||||||
for i in self.interface():
|
|
||||||
props[i] = AutomatedProperties._get_all_prop(self, i)
|
|
||||||
|
|
||||||
return self._ap_o_path, props
|
|
||||||
|
|
||||||
def set_interface(self, interface):
|
|
||||||
"""
|
|
||||||
With inheritance we can't easily tell what interfaces a class provides
|
|
||||||
so we will have each class that implements an interface tell the
|
|
||||||
base AutomatedProperties what it is they do provide. This is kind of
|
|
||||||
clunky and perhaps we can figure out a better way to do this later.
|
|
||||||
:param interface: An interface the object supports
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
if interface not in self._ap_interface:
|
|
||||||
self._ap_interface.append(interface)
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
|
||||||
def interface(self, all_interfaces=False):
|
|
||||||
if all_interfaces:
|
|
||||||
cpy = list(self._ap_interface)
|
|
||||||
cpy.extend(
|
|
||||||
["org.freedesktop.DBus.Introspectable",
|
|
||||||
"org.freedesktop.DBus.Properties"])
|
|
||||||
return cpy
|
|
||||||
|
|
||||||
return self._ap_interface
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_prop(obj, interface_name, property_name):
|
|
||||||
value = getattr(obj, property_name)
|
|
||||||
# Note: If we get an exception in this handler we won't know about it,
|
|
||||||
# only the side effect of no returned value!
|
|
||||||
log_debug('Get (%s), type (%s), value(%s)' %
|
|
||||||
(property_name, str(type(value)), str(value)))
|
|
||||||
return value
|
|
||||||
|
|
||||||
# Properties
|
|
||||||
# noinspection PyUnusedLocal
|
|
||||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
|
||||||
in_signature='ss', out_signature='v',
|
|
||||||
async_callbacks=('cb', 'cbe'))
|
|
||||||
def Get(self, interface_name, property_name, cb, cbe):
|
|
||||||
# Note: If we get an exception in this handler we won't know about it,
|
|
||||||
# only the side effect of no returned value!
|
|
||||||
r = cfg.create_request_entry(
|
|
||||||
-1, AutomatedProperties._get_prop,
|
|
||||||
(self, interface_name, property_name),
|
|
||||||
cb, cbe, False)
|
|
||||||
cfg.worker_q.put(r)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_all_prop(obj, interface_name):
|
|
||||||
if interface_name in obj.interface(True):
|
|
||||||
# Using introspection, lets build this dynamically
|
|
||||||
properties = get_properties(obj)
|
|
||||||
if interface_name in properties:
|
|
||||||
return properties[interface_name][1]
|
|
||||||
return {}
|
|
||||||
raise dbus.exceptions.DBusException(
|
|
||||||
obj._ap_interface,
|
|
||||||
'The object %s does not implement the %s interface'
|
|
||||||
% (obj.__class__, interface_name))
|
|
||||||
|
|
||||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
|
||||||
in_signature='s', out_signature='a{sv}',
|
|
||||||
async_callbacks=('cb', 'cbe'))
|
|
||||||
def GetAll(self, interface_name, cb, cbe):
|
|
||||||
r = cfg.create_request_entry(
|
|
||||||
-1, AutomatedProperties._get_all_prop,
|
|
||||||
(self, interface_name),
|
|
||||||
cb, cbe, False)
|
|
||||||
cfg.worker_q.put(r)
|
|
||||||
|
|
||||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
|
||||||
in_signature='ssv')
|
|
||||||
def Set(self, interface_name, property_name, new_value):
|
|
||||||
setattr(self, property_name, new_value)
|
|
||||||
self.PropertiesChanged(interface_name,
|
|
||||||
{property_name: new_value}, [])
|
|
||||||
|
|
||||||
# As dbus-python does not support introspection for properties we will
|
|
||||||
# get the autogenerated xml and then add our wanted properties to it.
|
|
||||||
@dbus.service.method(dbus_interface=dbus.INTROSPECTABLE_IFACE,
|
|
||||||
out_signature='s')
|
|
||||||
def Introspect(self):
|
|
||||||
r = dbus.service.Object.Introspect(self, self._ap_o_path, cfg.bus)
|
|
||||||
# Look at the properties in the class
|
|
||||||
props = get_properties(self)
|
|
||||||
|
|
||||||
for int_f, v in props.items():
|
|
||||||
r = add_properties(r, int_f, v[0])
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
@dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE,
|
|
||||||
signature='sa{sv}as')
|
|
||||||
def PropertiesChanged(self, interface_name, changed_properties,
|
|
||||||
invalidated_properties):
|
|
||||||
log_debug(('SIGNAL: PropertiesChanged(%s, %s, %s, %s)' %
|
|
||||||
(str(self._ap_o_path), str(interface_name),
|
|
||||||
str(changed_properties), str(invalidated_properties))))
|
|
||||||
|
|
||||||
def refresh(self, search_key=None, object_state=None):
|
|
||||||
"""
|
|
||||||
Take the values (properties) of an object and update them with what
|
|
||||||
lvm currently has. You can either fetch the new ones or supply the
|
|
||||||
new state to be updated with
|
|
||||||
:param search_key: The value to use to search for
|
|
||||||
:param object_state: Use this as the new object state
|
|
||||||
"""
|
|
||||||
num_changed = 0
|
|
||||||
|
|
||||||
# If we can't do a lookup, bail now, this happens if we blindly walk
|
|
||||||
# through all dbus objects as some don't have a search method, like
|
|
||||||
# 'Manager' object.
|
|
||||||
if not self._ap_search_method:
|
|
||||||
return
|
|
||||||
|
|
||||||
search = self.lvm_id
|
|
||||||
if search_key:
|
|
||||||
search = search_key
|
|
||||||
|
|
||||||
# Either we have the new object state or we need to go fetch it
|
|
||||||
if object_state:
|
|
||||||
new_state = object_state
|
|
||||||
else:
|
|
||||||
new_state = self._ap_search_method([search])[0]
|
|
||||||
assert isinstance(new_state, State)
|
|
||||||
|
|
||||||
assert new_state
|
|
||||||
|
|
||||||
# When we refresh an object the object identifiers might have changed
|
|
||||||
# because LVM allows the user to change them (name & uuid), thus if
|
|
||||||
# they have changed we need to update the object manager so that
|
|
||||||
# look-ups will happen correctly
|
|
||||||
old_id = self.state.identifiers()
|
|
||||||
new_id = new_state.identifiers()
|
|
||||||
if old_id[0] != new_id[0] or old_id[1] != new_id[1]:
|
|
||||||
cfg.om.lookup_update(self, new_id[0], new_id[1])
|
|
||||||
|
|
||||||
# Grab the properties values, then replace the state of the object
|
|
||||||
# and retrieve the new values.
|
|
||||||
o_prop = get_properties(self)
|
|
||||||
self.state = new_state
|
|
||||||
n_prop = get_properties(self)
|
|
||||||
|
|
||||||
changed = get_object_property_diff(o_prop, n_prop)
|
|
||||||
|
|
||||||
if changed:
|
|
||||||
for int_f, v in changed.items():
|
|
||||||
self.PropertiesChanged(int_f, v, [])
|
|
||||||
num_changed += 1
|
|
||||||
return num_changed
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
from . import cfg
|
|
||||||
from .cmdhandler import options_to_cli_args, LvmExecutionMeta
|
|
||||||
import dbus
|
|
||||||
from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug,\
|
|
||||||
add_no_notify
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
def pv_move_lv_cmd(move_options, lv_full_name,
|
|
||||||
pv_source, pv_source_range, pv_dest_range_list):
|
|
||||||
cmd = ['pvmove', '-i', '1']
|
|
||||||
cmd.extend(options_to_cli_args(move_options))
|
|
||||||
|
|
||||||
if lv_full_name:
|
|
||||||
cmd.extend(['-n', lv_full_name])
|
|
||||||
|
|
||||||
pv_range_append(cmd, pv_source, *pv_source_range)
|
|
||||||
pv_dest_ranges(cmd, pv_dest_range_list)
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
|
|
||||||
def lv_merge_cmd(merge_options, lv_full_name):
|
|
||||||
cmd = ['lvconvert', '--merge', '-i', '1']
|
|
||||||
cmd.extend(options_to_cli_args(merge_options))
|
|
||||||
cmd.append(lv_full_name)
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
|
|
||||||
def _move_merge(interface_name, command, job_state):
|
|
||||||
# We need to execute these command stand alone by forking & exec'ing
|
|
||||||
# the command always as we will be getting periodic output from them on
|
|
||||||
# the status of the long running operation.
|
|
||||||
command.insert(0, cfg.LVM_CMD)
|
|
||||||
|
|
||||||
# Instruct lvm to not register an event with us
|
|
||||||
command = add_no_notify(command)
|
|
||||||
|
|
||||||
#(self, start, ended, cmd, ec, stdout_txt, stderr_txt)
|
|
||||||
meta = LvmExecutionMeta(time.time(), 0, command, -1000, None, None)
|
|
||||||
|
|
||||||
cfg.blackbox.add(meta)
|
|
||||||
|
|
||||||
process = subprocess.Popen(command, stdout=subprocess.PIPE,
|
|
||||||
env=os.environ,
|
|
||||||
stderr=subprocess.PIPE, close_fds=True)
|
|
||||||
|
|
||||||
log_debug("Background process for %s is %d" %
|
|
||||||
(str(command), process.pid))
|
|
||||||
|
|
||||||
lines_iterator = iter(process.stdout.readline, b"")
|
|
||||||
for line in lines_iterator:
|
|
||||||
line_str = line.decode("utf-8")
|
|
||||||
|
|
||||||
# Check to see if the line has the correct number of separators
|
|
||||||
try:
|
|
||||||
if line_str.count(':') == 2:
|
|
||||||
(device, ignore, percentage) = line_str.split(':')
|
|
||||||
job_state.Percent = round(
|
|
||||||
float(percentage.strip()[:-1]), 1)
|
|
||||||
|
|
||||||
# While the move is in progress we need to periodically update
|
|
||||||
# the state to reflect where everything is at.
|
|
||||||
cfg.load()
|
|
||||||
except ValueError:
|
|
||||||
log_error("Trying to parse percentage which failed for %s" %
|
|
||||||
line_str)
|
|
||||||
|
|
||||||
out = process.communicate()
|
|
||||||
|
|
||||||
with meta.lock:
|
|
||||||
meta.ended = time.time()
|
|
||||||
meta.ec = process.returncode
|
|
||||||
meta.stderr_txt = out[1]
|
|
||||||
|
|
||||||
if process.returncode == 0:
|
|
||||||
job_state.Percent = 100
|
|
||||||
else:
|
|
||||||
raise dbus.exceptions.DBusException(
|
|
||||||
interface_name,
|
|
||||||
'Exit code %s, stderr = %s' % (str(process.returncode), out[1]))
|
|
||||||
|
|
||||||
cfg.load()
|
|
||||||
return '/'
|
|
||||||
|
|
||||||
|
|
||||||
def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
|
||||||
pv_dests_and_ranges, move_options, job_state):
|
|
||||||
"""
|
|
||||||
Common code for the pvmove handling.
|
|
||||||
:param interface_name: What dbus interface we are providing for
|
|
||||||
:param lv_name: Optional (None or name of LV to move)
|
|
||||||
:param pv_src_obj: dbus object patch for source PV
|
|
||||||
:param pv_source_range: (0,0 to ignore, else start, end segments)
|
|
||||||
:param pv_dests_and_ranges: Array of PV object paths and start/end segs
|
|
||||||
:param move_options: Hash with optional arguments
|
|
||||||
:param job_state: Used to convey information about jobs between processes
|
|
||||||
:return: '/' When complete, the empty object path
|
|
||||||
"""
|
|
||||||
pv_dests = []
|
|
||||||
pv_src = cfg.om.get_object_by_path(pv_src_obj)
|
|
||||||
if pv_src:
|
|
||||||
|
|
||||||
# Check to see if we are handling a move to a specific
|
|
||||||
# destination(s)
|
|
||||||
if len(pv_dests_and_ranges):
|
|
||||||
for pr in pv_dests_and_ranges:
|
|
||||||
pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
|
|
||||||
if not pv_dbus_obj:
|
|
||||||
raise dbus.exceptions.DBusException(
|
|
||||||
interface_name,
|
|
||||||
'PV Destination (%s) not found' % pr[0])
|
|
||||||
|
|
||||||
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
|
|
||||||
|
|
||||||
cmd = pv_move_lv_cmd(move_options,
|
|
||||||
lv_name,
|
|
||||||
pv_src.lvm_id,
|
|
||||||
pv_source_range,
|
|
||||||
pv_dests)
|
|
||||||
|
|
||||||
return _move_merge(interface_name, cmd, job_state)
|
|
||||||
else:
|
|
||||||
raise dbus.exceptions.DBusException(
|
|
||||||
interface_name, 'pv_src_obj (%s) not found' % pv_src_obj)
|
|
||||||
|
|
||||||
|
|
||||||
def merge(interface_name, lv_uuid, lv_name, merge_options, job_state):
|
|
||||||
# Make sure we have a dbus object representing it
|
|
||||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
||||||
if dbo:
|
|
||||||
cmd = lv_merge_cmd(merge_options, dbo.lvm_id)
|
|
||||||
return _move_merge(interface_name, cmd, job_state)
|
|
||||||
else:
|
|
||||||
raise dbus.exceptions.DBusException(
|
|
||||||
interface_name,
|
|
||||||
'LV with uuid %s and name %s not present!' % (lv_uuid, lv_name))
|
|
||||||
|
|
||||||
|
|
||||||
def _run_cmd(req):
|
|
||||||
log_debug(
|
|
||||||
"_run_cmd: Running method: %s with args %s" %
|
|
||||||
(str(req.method), str(req.arguments)))
|
|
||||||
req.run_cmd()
|
|
||||||
log_debug("_run_cmd: complete!")
|
|
||||||
|
|
||||||
|
|
||||||
def cmd_runner(request):
|
|
||||||
t = threading.Thread(target=_run_cmd, args=(request,))
|
|
||||||
t.start()
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import multiprocessing
|
|
||||||
import queue
|
|
||||||
import itertools
|
|
||||||
|
|
||||||
from lvmdbusd import path
|
|
||||||
|
|
||||||
LVM_CMD = os.getenv('LVM_BINARY', path.LVM_BINARY)
|
|
||||||
|
|
||||||
# This is the global object manager
|
|
||||||
om = None
|
|
||||||
|
|
||||||
# This is the global bus connection
|
|
||||||
bus = None
|
|
||||||
|
|
||||||
# Command line args
|
|
||||||
args = None
|
|
||||||
|
|
||||||
# Set to true if we are depending on external events for updates
|
|
||||||
got_external_event = False
|
|
||||||
|
|
||||||
# Shared state variable across all processes
|
|
||||||
run = multiprocessing.Value('i', 1)
|
|
||||||
|
|
||||||
# If this is set to true, the current setup support lvm shell and we are
|
|
||||||
# running in that mode of operation
|
|
||||||
SHELL_IN_USE = None
|
|
||||||
|
|
||||||
# Lock used by pprint
|
|
||||||
stdout_lock = multiprocessing.Lock()
|
|
||||||
|
|
||||||
worker_q = queue.Queue()
|
|
||||||
|
|
||||||
# Main event loop
|
|
||||||
loop = None
|
|
||||||
|
|
||||||
BUS_NAME = os.getenv('LVM_DBUS_NAME', 'com.redhat.lvmdbus1')
|
|
||||||
BASE_INTERFACE = 'com.redhat.lvmdbus1'
|
|
||||||
PV_INTERFACE = BASE_INTERFACE + '.Pv'
|
|
||||||
VG_INTERFACE = BASE_INTERFACE + '.Vg'
|
|
||||||
LV_INTERFACE = BASE_INTERFACE + '.Lv'
|
|
||||||
LV_COMMON_INTERFACE = BASE_INTERFACE + '.LvCommon'
|
|
||||||
THIN_POOL_INTERFACE = BASE_INTERFACE + '.ThinPool'
|
|
||||||
CACHE_POOL_INTERFACE = BASE_INTERFACE + '.CachePool'
|
|
||||||
LV_CACHED = BASE_INTERFACE + '.CachedLv'
|
|
||||||
SNAPSHOT_INTERFACE = BASE_INTERFACE + '.Snapshot'
|
|
||||||
MANAGER_INTERFACE = BASE_INTERFACE + '.Manager'
|
|
||||||
JOB_INTERFACE = BASE_INTERFACE + '.Job'
|
|
||||||
|
|
||||||
BASE_OBJ_PATH = '/' + BASE_INTERFACE.replace('.', '/')
|
|
||||||
PV_OBJ_PATH = BASE_OBJ_PATH + '/Pv'
|
|
||||||
VG_OBJ_PATH = BASE_OBJ_PATH + '/Vg'
|
|
||||||
LV_OBJ_PATH = BASE_OBJ_PATH + '/Lv'
|
|
||||||
THIN_POOL_PATH = BASE_OBJ_PATH + "/ThinPool"
|
|
||||||
CACHE_POOL_PATH = BASE_OBJ_PATH + "/CachePool"
|
|
||||||
HIDDEN_LV_PATH = BASE_OBJ_PATH + "/HiddenLv"
|
|
||||||
MANAGER_OBJ_PATH = BASE_OBJ_PATH + '/Manager'
|
|
||||||
JOB_OBJ_PATH = BASE_OBJ_PATH + '/Job'
|
|
||||||
|
|
||||||
# Counters for object path generation
|
|
||||||
pv_id = itertools.count()
|
|
||||||
vg_id = itertools.count()
|
|
||||||
lv_id = itertools.count()
|
|
||||||
thin_id = itertools.count()
|
|
||||||
cache_pool_id = itertools.count()
|
|
||||||
job_id = itertools.count()
|
|
||||||
hidden_lv = itertools.count()
|
|
||||||
|
|
||||||
# Used to prevent circular imports...
|
|
||||||
load = None
|
|
||||||
event = None
|
|
||||||
|
|
||||||
# Global cached state
|
|
||||||
db = None
|
|
||||||
|
|
||||||
# lvm flight recorder
|
|
||||||
blackbox = None
|
|
||||||
|
|
||||||
# RequestEntry ctor
|
|
||||||
create_request_entry = None
|
|
||||||
@@ -1,754 +0,0 @@
|
|||||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
import time
|
|
||||||
import threading
|
|
||||||
from itertools import chain
|
|
||||||
import collections
|
|
||||||
import traceback
|
|
||||||
import os
|
|
||||||
|
|
||||||
from lvmdbusd import cfg
|
|
||||||
from lvmdbusd.utils import pv_dest_ranges, log_debug, log_error, add_no_notify
|
|
||||||
from lvmdbusd.lvm_shell_proxy import LVMShellProxy
|
|
||||||
|
|
||||||
try:
|
|
||||||
import simplejson as json
|
|
||||||
except ImportError:
|
|
||||||
import json
|
|
||||||
|
|
||||||
SEP = '{|}'
|
|
||||||
|
|
||||||
total_time = 0.0
|
|
||||||
total_count = 0
|
|
||||||
|
|
||||||
# We need to prevent different threads from using the same lvm shell
|
|
||||||
# at the same time.
|
|
||||||
cmd_lock = threading.RLock()
|
|
||||||
|
|
||||||
|
|
||||||
class LvmExecutionMeta(object):
|
|
||||||
|
|
||||||
def __init__(self, start, ended, cmd, ec, stdout_txt, stderr_txt):
|
|
||||||
self.lock = threading.RLock()
|
|
||||||
self.start = start
|
|
||||||
self.ended = ended
|
|
||||||
self.cmd = cmd
|
|
||||||
self.ec = ec
|
|
||||||
self.stdout_txt = stdout_txt
|
|
||||||
self.stderr_txt = stderr_txt
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
with self.lock:
|
|
||||||
return "EC= %d for %s\n" \
|
|
||||||
"STARTED: %f, ENDED: %f\n" \
|
|
||||||
"STDOUT=%s\n" \
|
|
||||||
"STDERR=%s\n" % \
|
|
||||||
(self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt,
|
|
||||||
self.stderr_txt)
|
|
||||||
|
|
||||||
|
|
||||||
class LvmFlightRecorder(object):
|
|
||||||
|
|
||||||
def __init__(self, size=16):
|
|
||||||
self.queue = collections.deque(maxlen=size)
|
|
||||||
|
|
||||||
def add(self, lvm_exec_meta):
|
|
||||||
self.queue.append(lvm_exec_meta)
|
|
||||||
|
|
||||||
def dump(self):
|
|
||||||
with cmd_lock:
|
|
||||||
if len(self.queue):
|
|
||||||
log_error("LVM dbus flight recorder START")
|
|
||||||
for c in self.queue:
|
|
||||||
log_error(str(c))
|
|
||||||
log_error("LVM dbus flight recorder END")
|
|
||||||
|
|
||||||
|
|
||||||
cfg.blackbox = LvmFlightRecorder()
|
|
||||||
|
|
||||||
|
|
||||||
def _debug_c(cmd, exit_code, out):
|
|
||||||
log_error('CMD= %s' % ' '.join(cmd))
|
|
||||||
log_error(("EC= %d" % exit_code))
|
|
||||||
log_error(("STDOUT=\n %s\n" % out[0]))
|
|
||||||
log_error(("STDERR=\n %s\n" % out[1]))
|
|
||||||
|
|
||||||
|
|
||||||
def call_lvm(command, debug=False):
|
|
||||||
"""
|
|
||||||
Call an executable and return a tuple of exitcode, stdout, stderr
|
|
||||||
:param command: Command to execute
|
|
||||||
:param debug: Dump debug to stdout
|
|
||||||
"""
|
|
||||||
# print 'STACK:'
|
|
||||||
# for line in traceback.format_stack():
|
|
||||||
# print line.strip()
|
|
||||||
|
|
||||||
# Prepend the full lvm executable so that we can run different versions
|
|
||||||
# in different locations on the same box
|
|
||||||
command.insert(0, cfg.LVM_CMD)
|
|
||||||
command = add_no_notify(command)
|
|
||||||
|
|
||||||
process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True,
|
|
||||||
env=os.environ)
|
|
||||||
out = process.communicate()
|
|
||||||
|
|
||||||
stdout_text = bytes(out[0]).decode("utf-8")
|
|
||||||
stderr_text = bytes(out[1]).decode("utf-8")
|
|
||||||
|
|
||||||
if debug or process.returncode != 0:
|
|
||||||
_debug_c(command, process.returncode, (stdout_text, stderr_text))
|
|
||||||
|
|
||||||
return process.returncode, stdout_text, stderr_text
|
|
||||||
|
|
||||||
# The actual method which gets called to invoke the lvm command, can vary
|
|
||||||
# from forking a new process to using lvm shell
|
|
||||||
_t_call = call_lvm
|
|
||||||
|
|
||||||
|
|
||||||
def _shell_cfg():
|
|
||||||
global _t_call
|
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
|
||||||
lvm_shell = LVMShellProxy()
|
|
||||||
_t_call = lvm_shell.call_lvm
|
|
||||||
cfg.SHELL_IN_USE = lvm_shell
|
|
||||||
return True
|
|
||||||
except Exception:
|
|
||||||
_t_call = call_lvm
|
|
||||||
cfg.SHELL_IN_USE = None
|
|
||||||
log_error(traceback.format_exc())
|
|
||||||
log_error("Unable to utilize lvm shell, dropping back to fork & exec")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def set_execution(shell):
|
|
||||||
global _t_call
|
|
||||||
with cmd_lock:
|
|
||||||
# If the user requested lvm shell and we are currently setup that
|
|
||||||
# way, just return
|
|
||||||
if cfg.SHELL_IN_USE and shell:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
if not shell and cfg.SHELL_IN_USE:
|
|
||||||
cfg.SHELL_IN_USE.exit_shell()
|
|
||||||
cfg.SHELL_IN_USE = None
|
|
||||||
|
|
||||||
_t_call = call_lvm
|
|
||||||
if shell:
|
|
||||||
if cfg.args.use_json:
|
|
||||||
return _shell_cfg()
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def time_wrapper(command, debug=False):
|
|
||||||
global total_time
|
|
||||||
global total_count
|
|
||||||
|
|
||||||
with cmd_lock:
|
|
||||||
start = time.time()
|
|
||||||
results = _t_call(command, debug)
|
|
||||||
ended = time.time()
|
|
||||||
total_time += (ended - start)
|
|
||||||
total_count += 1
|
|
||||||
cfg.blackbox.add(LvmExecutionMeta(start, ended, command, *results))
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
call = time_wrapper
|
|
||||||
|
|
||||||
|
|
||||||
# Default cmd
|
|
||||||
# Place default arguments for every command here.
|
|
||||||
def _dc(cmd, args):
|
|
||||||
c = [cmd, '--noheading', '--separator', '%s' % SEP, '--nosuffix',
|
|
||||||
'--unbuffered', '--units', 'b']
|
|
||||||
c.extend(args)
|
|
||||||
return c
|
|
||||||
|
|
||||||
|
|
||||||
def parse(out):
|
|
||||||
rc = []
|
|
||||||
|
|
||||||
for line in out.split('\n'):
|
|
||||||
# This line includes separators, so process them
|
|
||||||
if SEP in line:
|
|
||||||
elem = line.split(SEP)
|
|
||||||
cleaned_elem = []
|
|
||||||
for e in elem:
|
|
||||||
e = e.strip()
|
|
||||||
cleaned_elem.append(e)
|
|
||||||
|
|
||||||
if len(cleaned_elem) > 1:
|
|
||||||
rc.append(cleaned_elem)
|
|
||||||
else:
|
|
||||||
t = line.strip()
|
|
||||||
if len(t) > 0:
|
|
||||||
rc.append(t)
|
|
||||||
return rc
|
|
||||||
|
|
||||||
|
|
||||||
def parse_column_names(out, column_names):
|
|
||||||
lines = parse(out)
|
|
||||||
rc = []
|
|
||||||
|
|
||||||
for i in range(0, len(lines)):
|
|
||||||
d = dict(list(zip(column_names, lines[i])))
|
|
||||||
rc.append(d)
|
|
||||||
|
|
||||||
return rc
|
|
||||||
|
|
||||||
|
|
||||||
def options_to_cli_args(options):
|
|
||||||
rc = []
|
|
||||||
for k, v in list(dict(options).items()):
|
|
||||||
if k.startswith("-"):
|
|
||||||
rc.append(k)
|
|
||||||
else:
|
|
||||||
rc.append("--%s" % k)
|
|
||||||
if v != "":
|
|
||||||
rc.append(str(v))
|
|
||||||
return rc
|
|
||||||
|
|
||||||
|
|
||||||
def pv_remove(device, remove_options):
|
|
||||||
cmd = ['pvremove']
|
|
||||||
cmd.extend(options_to_cli_args(remove_options))
|
|
||||||
cmd.append(device)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def _qt(tag_name):
|
|
||||||
return '@%s' % tag_name
|
|
||||||
|
|
||||||
|
|
||||||
def _tag(operation, what, add, rm, tag_options):
|
|
||||||
cmd = [operation]
|
|
||||||
cmd.extend(options_to_cli_args(tag_options))
|
|
||||||
|
|
||||||
if isinstance(what, list):
|
|
||||||
cmd.extend(what)
|
|
||||||
else:
|
|
||||||
cmd.append(what)
|
|
||||||
|
|
||||||
if add:
|
|
||||||
cmd.extend(list(chain.from_iterable(
|
|
||||||
('--addtag', _qt(x)) for x in add)))
|
|
||||||
if rm:
|
|
||||||
cmd.extend(list(chain.from_iterable(
|
|
||||||
('--deltag', _qt(x)) for x in rm)))
|
|
||||||
|
|
||||||
return call(cmd, False)
|
|
||||||
|
|
||||||
|
|
||||||
def pv_tag(pv_devices, add, rm, tag_options):
|
|
||||||
return _tag('pvchange', pv_devices, add, rm, tag_options)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_tag(vg_name, add, rm, tag_options):
|
|
||||||
return _tag('vgchange', vg_name, add, rm, tag_options)
|
|
||||||
|
|
||||||
|
|
||||||
def lv_tag(lv_name, add, rm, tag_options):
|
|
||||||
return _tag('lvchange', lv_name, add, rm, tag_options)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_rename(vg, new_name, rename_options):
|
|
||||||
cmd = ['vgrename']
|
|
||||||
cmd.extend(options_to_cli_args(rename_options))
|
|
||||||
cmd.extend([vg, new_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_remove(vg_name, remove_options):
|
|
||||||
cmd = ['vgremove']
|
|
||||||
cmd.extend(options_to_cli_args(remove_options))
|
|
||||||
cmd.extend(['-f', vg_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_lv_create(vg_name, create_options, name, size_bytes, pv_dests):
|
|
||||||
cmd = ['lvcreate']
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
|
||||||
cmd.extend(['--name', name, vg_name, '--yes'])
|
|
||||||
pv_dest_ranges(cmd, pv_dests)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_lv_snapshot(vg_name, snapshot_options, name, size_bytes):
|
|
||||||
cmd = ['lvcreate']
|
|
||||||
cmd.extend(options_to_cli_args(snapshot_options))
|
|
||||||
cmd.extend(["-s"])
|
|
||||||
|
|
||||||
if size_bytes != 0:
|
|
||||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
|
||||||
|
|
||||||
cmd.extend(['--name', name, vg_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool):
|
|
||||||
cmd = ['lvcreate']
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
|
|
||||||
if not thin_pool:
|
|
||||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
|
||||||
else:
|
|
||||||
cmd.extend(['--thin', '--size', str(size_bytes) + 'B'])
|
|
||||||
|
|
||||||
cmd.extend(['--yes'])
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
|
|
||||||
def vg_lv_create_linear(vg_name, create_options, name, size_bytes, thin_pool):
|
|
||||||
cmd = _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool)
|
|
||||||
cmd.extend(['--name', name, vg_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_lv_create_striped(vg_name, create_options, name, size_bytes,
|
|
||||||
num_stripes, stripe_size_kb, thin_pool):
|
|
||||||
cmd = _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool)
|
|
||||||
cmd.extend(['--stripes', str(num_stripes)])
|
|
||||||
|
|
||||||
if stripe_size_kb != 0:
|
|
||||||
cmd.extend(['--stripesize', str(stripe_size_kb)])
|
|
||||||
|
|
||||||
cmd.extend(['--name', name, vg_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def _vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
|
|
||||||
num_stripes, stripe_size_kb):
|
|
||||||
cmd = ['lvcreate']
|
|
||||||
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
|
|
||||||
cmd.extend(['--type', raid_type])
|
|
||||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
|
||||||
|
|
||||||
if num_stripes != 0:
|
|
||||||
cmd.extend(['--stripes', str(num_stripes)])
|
|
||||||
|
|
||||||
if stripe_size_kb != 0:
|
|
||||||
cmd.extend(['--stripesize', str(stripe_size_kb)])
|
|
||||||
|
|
||||||
cmd.extend(['--name', name, vg_name, '--yes'])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
|
|
||||||
num_stripes, stripe_size_kb):
|
|
||||||
cmd = ['lvcreate']
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
|
|
||||||
return _vg_lv_create_raid(vg_name, create_options, name, raid_type,
|
|
||||||
size_bytes, num_stripes, stripe_size_kb)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_lv_create_mirror(
|
|
||||||
vg_name, create_options, name, size_bytes, num_copies):
|
|
||||||
cmd = ['lvcreate']
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
|
|
||||||
cmd.extend(['--type', 'mirror'])
|
|
||||||
cmd.extend(['--mirrors', str(num_copies)])
|
|
||||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
|
||||||
cmd.extend(['--name', name, vg_name, '--yes'])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_create_cache_pool(md_full_name, data_full_name, create_options):
|
|
||||||
cmd = ['lvconvert']
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
cmd.extend(['--type', 'cache-pool', '--force', '-y',
|
|
||||||
'--poolmetadata', md_full_name, data_full_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_create_thin_pool(md_full_name, data_full_name, create_options):
|
|
||||||
cmd = ['lvconvert']
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
cmd.extend(['--type', 'thin-pool', '--force', '-y',
|
|
||||||
'--poolmetadata', md_full_name, data_full_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def lv_remove(lv_path, remove_options):
|
|
||||||
cmd = ['lvremove']
|
|
||||||
cmd.extend(options_to_cli_args(remove_options))
|
|
||||||
cmd.extend(['-f', lv_path])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def lv_rename(lv_path, new_name, rename_options):
|
|
||||||
cmd = ['lvrename']
|
|
||||||
cmd.extend(options_to_cli_args(rename_options))
|
|
||||||
cmd.extend([lv_path, new_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def lv_resize(lv_full_name, size_change, pv_dests,
|
|
||||||
resize_options):
|
|
||||||
cmd = ['lvresize', '--force']
|
|
||||||
|
|
||||||
cmd.extend(options_to_cli_args(resize_options))
|
|
||||||
|
|
||||||
if size_change < 0:
|
|
||||||
cmd.append("-L-%dB" % (-size_change))
|
|
||||||
else:
|
|
||||||
cmd.append("-L+%dB" % (size_change))
|
|
||||||
|
|
||||||
cmd.append(lv_full_name)
|
|
||||||
pv_dest_ranges(cmd, pv_dests)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def lv_lv_create(lv_full_name, create_options, name, size_bytes):
|
|
||||||
cmd = ['lvcreate']
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
cmd.extend(['--virtualsize', str(size_bytes) + 'B', '-T'])
|
|
||||||
cmd.extend(['--name', name, lv_full_name, '--yes'])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def lv_cache_lv(cache_pool_full_name, lv_full_name, cache_options):
|
|
||||||
# lvconvert --type cache --cachepool VG/CachePoolLV VG/OriginLV
|
|
||||||
cmd = ['lvconvert']
|
|
||||||
cmd.extend(options_to_cli_args(cache_options))
|
|
||||||
cmd.extend(['-y', '--type', 'cache', '--cachepool',
|
|
||||||
cache_pool_full_name, lv_full_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def lv_detach_cache(lv_full_name, detach_options, destroy_cache):
|
|
||||||
cmd = ['lvconvert']
|
|
||||||
if destroy_cache:
|
|
||||||
option = '--uncache'
|
|
||||||
else:
|
|
||||||
# Currently fairly dangerous
|
|
||||||
# see: https://bugzilla.redhat.com/show_bug.cgi?id=1248972
|
|
||||||
option = '--splitcache'
|
|
||||||
cmd.extend(options_to_cli_args(detach_options))
|
|
||||||
# needed to prevent interactive questions
|
|
||||||
cmd.extend(["--yes", "--force"])
|
|
||||||
cmd.extend([option, lv_full_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def supports_json():
|
|
||||||
cmd = ['help']
|
|
||||||
rc, out, err = call(cmd)
|
|
||||||
if rc == 0:
|
|
||||||
if cfg.SHELL_IN_USE:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
if 'fullreport' in err:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def lvm_full_report_json():
|
|
||||||
pv_columns = ['pv_name', 'pv_uuid', 'pv_fmt', 'pv_size', 'pv_free',
|
|
||||||
'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free',
|
|
||||||
'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
|
|
||||||
'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
|
|
||||||
'vg_uuid', 'pv_missing']
|
|
||||||
|
|
||||||
pv_seg_columns = ['pvseg_start', 'pvseg_size', 'segtype',
|
|
||||||
'pv_uuid', 'lv_uuid', 'pv_name']
|
|
||||||
|
|
||||||
vg_columns = ['vg_name', 'vg_uuid', 'vg_fmt', 'vg_size', 'vg_free',
|
|
||||||
'vg_sysid', 'vg_extent_size', 'vg_extent_count',
|
|
||||||
'vg_free_count', 'vg_profile', 'max_lv', 'max_pv',
|
|
||||||
'pv_count', 'lv_count', 'snap_count', 'vg_seqno',
|
|
||||||
'vg_mda_count', 'vg_mda_free', 'vg_mda_size',
|
|
||||||
'vg_mda_used_count', 'vg_attr', 'vg_tags']
|
|
||||||
|
|
||||||
lv_columns = ['lv_uuid', 'lv_name', 'lv_path', 'lv_size',
|
|
||||||
'vg_name', 'pool_lv_uuid', 'pool_lv', 'origin_uuid',
|
|
||||||
'origin', 'data_percent',
|
|
||||||
'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
|
|
||||||
'metadata_lv', 'lv_parent', 'lv_role', 'lv_layout',
|
|
||||||
'snap_percent', 'metadata_percent', 'copy_percent',
|
|
||||||
'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
|
|
||||||
|
|
||||||
lv_seg_columns = ['seg_pe_ranges', 'segtype', 'lv_uuid']
|
|
||||||
|
|
||||||
cmd = _dc('fullreport', [
|
|
||||||
'-a', # Need hidden too
|
|
||||||
'--configreport', 'pv', '-o', ','.join(pv_columns),
|
|
||||||
'--configreport', 'vg', '-o', ','.join(vg_columns),
|
|
||||||
'--configreport', 'lv', '-o', ','.join(lv_columns),
|
|
||||||
'--configreport', 'seg', '-o', ','.join(lv_seg_columns),
|
|
||||||
'--configreport', 'pvseg', '-o', ','.join(pv_seg_columns),
|
|
||||||
'--reportformat', 'json'
|
|
||||||
])
|
|
||||||
|
|
||||||
rc, out, err = call(cmd)
|
|
||||||
if rc == 0:
|
|
||||||
# With the current implementation, if we are using the shell then we
|
|
||||||
# are using JSON and JSON is returned back to us as it was parsed to
|
|
||||||
# figure out if we completed OK or not
|
|
||||||
if cfg.SHELL_IN_USE:
|
|
||||||
assert(type(out) == dict)
|
|
||||||
return out
|
|
||||||
else:
|
|
||||||
return json.loads(out)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def pv_retrieve_with_segs(device=None):
|
|
||||||
d = []
|
|
||||||
err = ""
|
|
||||||
out = ""
|
|
||||||
rc = 0
|
|
||||||
|
|
||||||
columns = ['pv_name', 'pv_uuid', 'pv_fmt', 'pv_size', 'pv_free',
|
|
||||||
'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free',
|
|
||||||
'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
|
|
||||||
'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
|
|
||||||
'vg_uuid', 'pvseg_start', 'pvseg_size', 'segtype', 'pv_missing']
|
|
||||||
|
|
||||||
# Lvm has some issues where it returns failure when querying pvs when other
|
|
||||||
# operations are in process, see:
|
|
||||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1274085
|
|
||||||
for i in range(0, 10):
|
|
||||||
cmd = _dc('pvs', ['-o', ','.join(columns)])
|
|
||||||
|
|
||||||
if device:
|
|
||||||
cmd.extend(device)
|
|
||||||
|
|
||||||
rc, out, err = call(cmd)
|
|
||||||
|
|
||||||
if rc == 0:
|
|
||||||
d = parse_column_names(out, columns)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
time.sleep(0.2)
|
|
||||||
log_debug("LVM Bug workaround, retrying pvs command...")
|
|
||||||
|
|
||||||
if rc != 0:
|
|
||||||
msg = "We were unable to get pvs to return without error after " \
|
|
||||||
"trying 10 times, RC=%d, STDERR=(%s), STDOUT=(%s)" % \
|
|
||||||
(rc, err, out)
|
|
||||||
log_error(msg)
|
|
||||||
raise RuntimeError(msg)
|
|
||||||
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def pv_resize(device, size_bytes, create_options):
|
|
||||||
cmd = ['pvresize']
|
|
||||||
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
|
|
||||||
if size_bytes != 0:
|
|
||||||
cmd.extend(['--yes', '--setphysicalvolumesize', str(size_bytes) + 'B'])
|
|
||||||
|
|
||||||
cmd.extend([device])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def pv_create(create_options, devices):
|
|
||||||
cmd = ['pvcreate', '-ff']
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
cmd.extend(devices)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def pv_allocatable(device, yes, allocation_options):
|
|
||||||
yn = 'n'
|
|
||||||
|
|
||||||
if yes:
|
|
||||||
yn = 'y'
|
|
||||||
|
|
||||||
cmd = ['pvchange']
|
|
||||||
cmd.extend(options_to_cli_args(allocation_options))
|
|
||||||
cmd.extend(['-x', yn, device])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def pv_scan(activate, cache, device_paths, major_minors, scan_options):
|
|
||||||
cmd = ['pvscan']
|
|
||||||
cmd.extend(options_to_cli_args(scan_options))
|
|
||||||
|
|
||||||
if activate:
|
|
||||||
cmd.extend(['--activate', "ay"])
|
|
||||||
|
|
||||||
if cache:
|
|
||||||
cmd.append('--cache')
|
|
||||||
|
|
||||||
if len(device_paths) > 0:
|
|
||||||
for d in device_paths:
|
|
||||||
cmd.append(d)
|
|
||||||
|
|
||||||
if len(major_minors) > 0:
|
|
||||||
for mm in major_minors:
|
|
||||||
cmd.append("%s:%s" % (mm))
|
|
||||||
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_create(create_options, pv_devices, name):
|
|
||||||
cmd = ['vgcreate']
|
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
|
||||||
cmd.append(name)
|
|
||||||
cmd.extend(pv_devices)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_change(change_options, name):
|
|
||||||
cmd = ['vgchange']
|
|
||||||
cmd.extend(options_to_cli_args(change_options))
|
|
||||||
cmd.append(name)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_reduce(vg_name, missing, pv_devices, reduce_options):
|
|
||||||
cmd = ['vgreduce']
|
|
||||||
cmd.extend(options_to_cli_args(reduce_options))
|
|
||||||
|
|
||||||
if missing:
|
|
||||||
cmd.append('--removemissing')
|
|
||||||
elif len(pv_devices) == 0:
|
|
||||||
cmd.append('--all')
|
|
||||||
|
|
||||||
cmd.append(vg_name)
|
|
||||||
cmd.extend(pv_devices)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_extend(vg_name, extend_devices, extend_options):
|
|
||||||
cmd = ['vgextend']
|
|
||||||
cmd.extend(options_to_cli_args(extend_options))
|
|
||||||
cmd.append(vg_name)
|
|
||||||
cmd.extend(extend_devices)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def _vg_value_set(name, arguments, options):
|
|
||||||
cmd = ['vgchange']
|
|
||||||
cmd.extend(options_to_cli_args(options))
|
|
||||||
cmd.append(name)
|
|
||||||
cmd.extend(arguments)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_allocation_policy(vg_name, policy, policy_options):
|
|
||||||
return _vg_value_set(vg_name, ['--alloc', policy], policy_options)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_max_pv(vg_name, number, max_options):
|
|
||||||
return _vg_value_set(vg_name, ['--maxphysicalvolumes', str(number)],
|
|
||||||
max_options)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_max_lv(vg_name, number, max_options):
|
|
||||||
return _vg_value_set(vg_name, ['-l', str(number)], max_options)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_uuid_gen(vg_name, ignore, options):
|
|
||||||
assert ignore is None
|
|
||||||
return _vg_value_set(vg_name, ['--uuid'], options)
|
|
||||||
|
|
||||||
|
|
||||||
def activate_deactivate(op, name, activate, control_flags, options):
|
|
||||||
cmd = [op]
|
|
||||||
cmd.extend(options_to_cli_args(options))
|
|
||||||
|
|
||||||
op = '-a'
|
|
||||||
|
|
||||||
if control_flags:
|
|
||||||
# Autoactivation
|
|
||||||
if (1 << 0) & control_flags:
|
|
||||||
op += 'a'
|
|
||||||
# Exclusive locking (Cluster)
|
|
||||||
if (1 << 1) & control_flags:
|
|
||||||
op += 'e'
|
|
||||||
|
|
||||||
# Local node activation
|
|
||||||
if (1 << 2) & control_flags:
|
|
||||||
op += 'l'
|
|
||||||
|
|
||||||
# Activation modes
|
|
||||||
if (1 << 3) & control_flags:
|
|
||||||
cmd.extend(['--activationmode', 'complete'])
|
|
||||||
elif (1 << 4) & control_flags:
|
|
||||||
cmd.extend(['--activationmode', 'partial'])
|
|
||||||
|
|
||||||
# Ignore activation skip
|
|
||||||
if (1 << 5) & control_flags:
|
|
||||||
cmd.append('--ignoreactivationskip')
|
|
||||||
|
|
||||||
if activate:
|
|
||||||
op += 'y'
|
|
||||||
else:
|
|
||||||
op += 'n'
|
|
||||||
|
|
||||||
cmd.append(op)
|
|
||||||
cmd.append(name)
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_retrieve(vg_specific):
|
|
||||||
if vg_specific:
|
|
||||||
assert isinstance(vg_specific, list)
|
|
||||||
|
|
||||||
columns = ['vg_name', 'vg_uuid', 'vg_fmt', 'vg_size', 'vg_free',
|
|
||||||
'vg_sysid', 'vg_extent_size', 'vg_extent_count',
|
|
||||||
'vg_free_count', 'vg_profile', 'max_lv', 'max_pv',
|
|
||||||
'pv_count', 'lv_count', 'snap_count', 'vg_seqno',
|
|
||||||
'vg_mda_count', 'vg_mda_free', 'vg_mda_size',
|
|
||||||
'vg_mda_used_count', 'vg_attr', 'vg_tags']
|
|
||||||
|
|
||||||
cmd = _dc('vgs', ['-o', ','.join(columns)])
|
|
||||||
|
|
||||||
if vg_specific:
|
|
||||||
cmd.extend(vg_specific)
|
|
||||||
|
|
||||||
d = []
|
|
||||||
rc, out, err = call(cmd)
|
|
||||||
if rc == 0:
|
|
||||||
d = parse_column_names(out, columns)
|
|
||||||
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def lv_retrieve_with_segments():
|
|
||||||
columns = ['lv_uuid', 'lv_name', 'lv_path', 'lv_size',
|
|
||||||
'vg_name', 'pool_lv_uuid', 'pool_lv', 'origin_uuid',
|
|
||||||
'origin', 'data_percent',
|
|
||||||
'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
|
|
||||||
'metadata_lv', 'seg_pe_ranges', 'segtype', 'lv_parent',
|
|
||||||
'lv_role', 'lv_layout',
|
|
||||||
'snap_percent', 'metadata_percent', 'copy_percent',
|
|
||||||
'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
|
|
||||||
|
|
||||||
cmd = _dc('lvs', ['-a', '-o', ','.join(columns)])
|
|
||||||
rc, out, err = call(cmd)
|
|
||||||
|
|
||||||
d = []
|
|
||||||
|
|
||||||
if rc == 0:
|
|
||||||
d = parse_column_names(out, columns)
|
|
||||||
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pv_data = pv_retrieve_with_segs()
|
|
||||||
|
|
||||||
for p in pv_data:
|
|
||||||
print(str(p))
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from .pv import load_pvs
|
|
||||||
from .vg import load_vgs
|
|
||||||
from .lv import load_lvs
|
|
||||||
from . import cfg
|
|
||||||
from .utils import MThreadRunner, log_debug, log_error
|
|
||||||
import threading
|
|
||||||
import queue
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
|
|
||||||
def _main_thread_load(refresh=True, emit_signal=True):
|
|
||||||
num_total_changes = 0
|
|
||||||
|
|
||||||
num_total_changes += load_pvs(
|
|
||||||
refresh=refresh,
|
|
||||||
emit_signal=emit_signal,
|
|
||||||
cache_refresh=False)[1]
|
|
||||||
num_total_changes += load_vgs(
|
|
||||||
refresh=refresh,
|
|
||||||
emit_signal=emit_signal,
|
|
||||||
cache_refresh=False)[1]
|
|
||||||
num_total_changes += load_lvs(
|
|
||||||
refresh=refresh,
|
|
||||||
emit_signal=emit_signal,
|
|
||||||
cache_refresh=False)[1]
|
|
||||||
|
|
||||||
return num_total_changes
|
|
||||||
|
|
||||||
|
|
||||||
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True,
|
|
||||||
need_main_thread=True):
|
|
||||||
# Go through and load all the PVs, VGs and LVs
|
|
||||||
if cache_refresh:
|
|
||||||
cfg.db.refresh(log)
|
|
||||||
|
|
||||||
if need_main_thread:
|
|
||||||
rc = MThreadRunner(_main_thread_load, refresh, emit_signal).done()
|
|
||||||
else:
|
|
||||||
rc = _main_thread_load(refresh, emit_signal)
|
|
||||||
|
|
||||||
return rc
|
|
||||||
|
|
||||||
|
|
||||||
# Even though lvm can handle multiple changes concurrently it really doesn't
|
|
||||||
# make sense to make a 1-1 fetch of data for each change of lvm because when
|
|
||||||
# we fetch the data once all previous changes are reflected.
|
|
||||||
class StateUpdate(object):
|
|
||||||
|
|
||||||
class UpdateRequest(object):
|
|
||||||
|
|
||||||
def __init__(self, refresh, emit_signal, cache_refresh, log,
|
|
||||||
need_main_thread):
|
|
||||||
self.is_done = False
|
|
||||||
self.refresh = refresh
|
|
||||||
self.emit_signal = emit_signal
|
|
||||||
self.cache_refresh = cache_refresh
|
|
||||||
self.log = log
|
|
||||||
self.need_main_thread = need_main_thread
|
|
||||||
self.result = None
|
|
||||||
self.cond = threading.Condition(threading.Lock())
|
|
||||||
|
|
||||||
def done(self):
|
|
||||||
with self.cond:
|
|
||||||
if not self.is_done:
|
|
||||||
self.cond.wait()
|
|
||||||
return self.result
|
|
||||||
|
|
||||||
def set_result(self, result):
|
|
||||||
with self.cond:
|
|
||||||
self.result = result
|
|
||||||
self.is_done = True
|
|
||||||
self.cond.notify_all()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def update_thread(obj):
|
|
||||||
queued_requests = []
|
|
||||||
while cfg.run.value != 0:
|
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
|
||||||
refresh = True
|
|
||||||
emit_signal = True
|
|
||||||
cache_refresh = True
|
|
||||||
log = True
|
|
||||||
need_main_thread = True
|
|
||||||
|
|
||||||
with obj.lock:
|
|
||||||
wait = not obj.deferred
|
|
||||||
obj.deferred = False
|
|
||||||
|
|
||||||
if len(queued_requests) == 0 and wait:
|
|
||||||
queued_requests.append(obj.queue.get(True, 2))
|
|
||||||
|
|
||||||
# Ok we have one or the deferred queue has some,
|
|
||||||
# check if any others
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
queued_requests.append(obj.queue.get(False))
|
|
||||||
|
|
||||||
except queue.Empty:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if len(queued_requests) > 1:
|
|
||||||
log_debug("Processing %d updates!" % len(queued_requests),
|
|
||||||
'bg_black', 'fg_light_green')
|
|
||||||
|
|
||||||
# We have what we can, run the update with the needed options
|
|
||||||
for i in queued_requests:
|
|
||||||
if not i.refresh:
|
|
||||||
refresh = False
|
|
||||||
if not i.emit_signal:
|
|
||||||
emit_signal = False
|
|
||||||
if not i.cache_refresh:
|
|
||||||
cache_refresh = False
|
|
||||||
if not i.log:
|
|
||||||
log = False
|
|
||||||
if not i.need_main_thread:
|
|
||||||
need_main_thread = False
|
|
||||||
|
|
||||||
num_changes = load(refresh, emit_signal, cache_refresh, log,
|
|
||||||
need_main_thread)
|
|
||||||
# Update is done, let everyone know!
|
|
||||||
for i in queued_requests:
|
|
||||||
i.set_result(num_changes)
|
|
||||||
|
|
||||||
# Only clear out the requests after we have given them a result
|
|
||||||
# otherwise we can orphan the waiting threads and they never
|
|
||||||
# wake up if we get an exception
|
|
||||||
queued_requests = []
|
|
||||||
|
|
||||||
except queue.Empty:
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
st = traceback.format_exc()
|
|
||||||
log_error("update_thread exception: \n%s" % st)
|
|
||||||
cfg.blackbox.dump()
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.lock = threading.RLock()
|
|
||||||
self.queue = queue.Queue()
|
|
||||||
self.deferred = False
|
|
||||||
|
|
||||||
# Do initial load
|
|
||||||
load(refresh=False, emit_signal=False, need_main_thread=False)
|
|
||||||
|
|
||||||
self.thread = threading.Thread(target=StateUpdate.update_thread,
|
|
||||||
args=(self,))
|
|
||||||
|
|
||||||
def load(self, refresh=True, emit_signal=True, cache_refresh=True,
|
|
||||||
log=True, need_main_thread=True):
|
|
||||||
# Place this request on the queue and wait for it to be completed
|
|
||||||
req = StateUpdate.UpdateRequest(refresh, emit_signal, cache_refresh,
|
|
||||||
log, need_main_thread)
|
|
||||||
self.queue.put(req)
|
|
||||||
return req.done()
|
|
||||||
|
|
||||||
def event(self):
|
|
||||||
with self.lock:
|
|
||||||
self.deferred = True
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user