mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-10-30 20:23:49 +03:00 
			
		
		
		
	Compare commits
	
		
			912 Commits
		
	
	
		
			dev-bmr-dm
			...
			old-beta5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | 
							
								
								
									
										30
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,30 +0,0 @@ | ||||
| *.5 | ||||
| *.7 | ||||
| *.8 | ||||
| *.a | ||||
| *.d | ||||
| *.o | ||||
| *.orig | ||||
| *.pc | ||||
| *.pot | ||||
| *.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. | ||||
							
								
								
									
										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. | ||||
|  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | ||||
| 		  GNU LIBRARY GENERAL PUBLIC LICENSE | ||||
| 		       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 | ||||
|  of this license document, but changing it is not allowed. | ||||
|  | ||||
| [This is the first released version of the Lesser GPL.  It also counts | ||||
|  as the successor of the GNU Library Public License, version 2, hence | ||||
|  the version number 2.1.] | ||||
| [This is the first released version of the library GPL.  It is | ||||
|  numbered 2 because it goes with version 2 of the ordinary GPL.] | ||||
|  | ||||
| 			    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 | ||||
| free software--to make sure the software is free for all its users. | ||||
|  | ||||
|   This license, the Lesser General Public License, applies to some | ||||
| specially designated software packages--typically libraries--of the | ||||
| Free Software Foundation and other authors who decide to use it.  You | ||||
| can use it too, but we suggest you first think carefully about whether | ||||
| this license or the ordinary General Public License is the better | ||||
| strategy to use in any particular case, based on the explanations below. | ||||
|   This license, the Library General Public License, applies to some | ||||
| specially designated Free Software Foundation software, and to any | ||||
| other libraries whose authors decide to use it.  You can use it for | ||||
| your libraries, too. | ||||
|  | ||||
|   When we speak of free software, we are referring to freedom of use, | ||||
| not price.  Our General Public Licenses are designed to make sure that | ||||
| you have the freedom to distribute copies of free software (and charge | ||||
| for this service if you wish); that you receive source code or can get | ||||
| it if you want it; that you can change the software and use pieces of | ||||
| it in new free programs; and that you are informed that you can do | ||||
| these things. | ||||
|   When we speak of free software, we are referring to freedom, not | ||||
| price.  Our General Public Licenses are designed to make sure that you | ||||
| have the freedom to distribute copies of free software (and charge for | ||||
| this service if you wish), that you receive source code or can get it | ||||
| if you want it, that you can change the software or use pieces of it | ||||
| in new free programs; and that you know you can do these things. | ||||
|  | ||||
|   To protect your rights, we need to make restrictions that forbid | ||||
| distributors to deny you these rights or to ask you to surrender these | ||||
| rights.  These restrictions translate to certain responsibilities for | ||||
| you if you distribute copies of the library or if you modify it. | ||||
| anyone to deny you these rights or to ask you to surrender the rights. | ||||
| These restrictions translate to certain responsibilities for you if | ||||
| you distribute copies of the library, or if you modify it. | ||||
|  | ||||
|   For example, if you distribute copies of the library, whether gratis | ||||
| or for a fee, you must give the recipients all the rights that we gave | ||||
| you.  You must make sure that they, too, receive or can get the source | ||||
| code.  If you link other code with the library, you must provide | ||||
| complete object files to the recipients, so that they can relink them | ||||
| with the library after making changes to the library and recompiling | ||||
| code.  If you link a program with the library, you must provide | ||||
| complete object files to the recipients so that they can relink them | ||||
| with the library, after making changes to the library and recompiling | ||||
| it.  And you must show them these terms so they know their rights. | ||||
|  | ||||
|   We protect your rights with a two-step method: (1) we copyright the | ||||
| library, and (2) we offer you this license, which gives you legal | ||||
|   Our method of protecting your rights has two steps: (1) copyright | ||||
| the library, and (2) offer you this license which gives you legal | ||||
| permission to copy, distribute and/or modify the library. | ||||
|  | ||||
|   To protect each distributor, we want to make it very clear that | ||||
| there is no warranty for the free library.  Also, if the library is | ||||
| modified by someone else and passed on, the recipients should know | ||||
| that what they have is not the original version, so that the original | ||||
| author's reputation will not be affected by problems that might be | ||||
| introduced by others. | ||||
|   Also, for each distributor's protection, we want to make certain | ||||
| that everyone understands that there is no warranty for this free | ||||
| library.  If the library is modified by someone else and passed on, we | ||||
| want its recipients to know that what they have is not the original | ||||
| version, so that any problems introduced by others will not reflect on | ||||
| the original authors' reputations. | ||||
|  | ||||
|   Finally, software patents pose a constant threat to the existence of | ||||
| any free program.  We wish to make sure that a company cannot | ||||
| effectively restrict the users of a free program by obtaining a | ||||
| restrictive license from a patent holder.  Therefore, we insist that | ||||
| any patent license obtained for a version of the library must be | ||||
| consistent with the full freedom of use specified in this license. | ||||
|   Finally, any free program is threatened constantly by software | ||||
| patents.  We wish to avoid the danger that companies distributing free | ||||
| software will individually obtain patent licenses, thus in effect | ||||
| transforming the program into proprietary software.  To prevent this, | ||||
| we have made it clear that any patent must be licensed for everyone's | ||||
| free use or not licensed at all. | ||||
|  | ||||
|   Most GNU software, including some libraries, is covered by the | ||||
| ordinary GNU General Public License.  This license, the GNU Lesser | ||||
| General Public License, applies to certain designated libraries, and | ||||
| is quite different from the ordinary General Public License.  We use | ||||
| this license for certain libraries in order to permit linking those | ||||
| libraries into non-free programs. | ||||
|   Most GNU software, including some libraries, is covered by the ordinary | ||||
| GNU General Public License, which was designed for utility programs.  This | ||||
| license, the GNU Library General Public License, applies to certain | ||||
| designated libraries.  This license is quite different from the ordinary | ||||
| one; be sure to read it in full, and don't assume that anything in it is | ||||
| the same as in the ordinary license. | ||||
|  | ||||
|   When a program is linked with a library, whether statically or using | ||||
| a shared library, the combination of the two is legally speaking a | ||||
| combined work, a derivative of the original library.  The ordinary | ||||
| General Public License therefore permits such linking only if the | ||||
| entire combination fits its criteria of freedom.  The Lesser General | ||||
| Public License permits more lax criteria for linking other code with | ||||
| the library. | ||||
|   The reason we have a separate public license for some libraries is that | ||||
| they blur the distinction we usually make between modifying or adding to a | ||||
| program and simply using it.  Linking a program with a library, without | ||||
| changing the library, is in some sense simply using the library, and is | ||||
| analogous to running a utility program or application program.  However, in | ||||
| a textual and legal sense, the linked executable is a combined work, a | ||||
| derivative of the original library, and the ordinary General Public License | ||||
| treats it as such. | ||||
|  | ||||
|   We call this license the "Lesser" General Public License because it | ||||
| does Less to protect the user's freedom than the ordinary General | ||||
| Public License.  It also provides other free software developers Less | ||||
| of an advantage over competing non-free programs.  These disadvantages | ||||
| are the reason we use the ordinary General Public License for many | ||||
| libraries.  However, the Lesser license provides advantages in certain | ||||
| special circumstances. | ||||
|   Because of this blurred distinction, using the ordinary General | ||||
| Public License for libraries did not effectively promote software | ||||
| sharing, because most developers did not use the libraries.  We | ||||
| concluded that weaker conditions might promote sharing better. | ||||
|  | ||||
|   For example, on rare occasions, there may be a special need to | ||||
| encourage the widest possible use of a certain library, so that it becomes | ||||
| a de-facto standard.  To achieve this, non-free programs must be | ||||
| allowed to use the library.  A more frequent case is that a free | ||||
| library does the same job as widely used non-free libraries.  In this | ||||
| case, there is little to gain by limiting the free library to free | ||||
| software only, so we use the Lesser General Public License. | ||||
|  | ||||
|   In other cases, permission to use a particular library in non-free | ||||
| programs enables a greater number of people to use a large body of | ||||
| free software.  For example, permission to use the GNU C Library in | ||||
| non-free programs enables many more people to use the whole GNU | ||||
| operating system, as well as its variant, the GNU/Linux operating | ||||
| system. | ||||
|  | ||||
|   Although the Lesser General Public License is Less protective of the | ||||
| users' freedom, it does ensure that the user of a program that is | ||||
| linked with the Library has the freedom and the wherewithal to run | ||||
| that program using a modified version of the Library. | ||||
|   However, unrestricted linking of non-free programs would deprive the | ||||
| users of those programs of all benefit from the free status of the | ||||
| libraries themselves.  This Library General Public License is intended to | ||||
| permit developers of non-free programs to use free libraries, while | ||||
| preserving your freedom as a user of such programs to change the free | ||||
| libraries that are incorporated in them.  (We have not seen how to achieve | ||||
| this as regards changes in header files, but we have achieved it as regards | ||||
| changes in the actual functions of the Library.)  The hope is that this | ||||
| will lead to faster development of free libraries. | ||||
|  | ||||
|   The precise terms and conditions for copying, distribution and | ||||
| modification follow.  Pay close attention to the difference between a | ||||
| "work based on the library" and a "work that uses the library".  The | ||||
| former contains code derived from the library, whereas the latter must | ||||
| be combined with the library in order to run. | ||||
| former contains code derived from the library, while the latter only | ||||
| works together with the library. | ||||
|  | ||||
|   Note that it is possible for a library to be covered by the ordinary | ||||
| General Public License rather than by this special one. | ||||
|  | ||||
| 		  GNU LESSER GENERAL PUBLIC LICENSE | ||||
| 		  GNU LIBRARY GENERAL PUBLIC LICENSE | ||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
|  | ||||
|   0. This License Agreement applies to any software library or other | ||||
| program which contains a notice placed by the copyright holder or | ||||
| other authorized party saying it may be distributed under the terms of | ||||
| this Lesser General Public License (also called "this License"). | ||||
| Each licensee is addressed as "you". | ||||
|   0. This License Agreement applies to any software library which | ||||
| contains a notice placed by the copyright holder or other authorized | ||||
| party saying it may be distributed under the terms of this Library | ||||
| General Public License (also called "this License").  Each licensee is | ||||
| addressed as "you". | ||||
|  | ||||
|   A "library" means a collection of software functions and/or data | ||||
| 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, | ||||
| 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 | ||||
| work containing portions of the Library, and distribute that work | ||||
| 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 | ||||
|     to use the modified definitions.) | ||||
|  | ||||
|     b) Use a suitable shared library mechanism for linking with the | ||||
|     Library.  A suitable mechanism is one that (1) uses at run time a | ||||
|     copy of the library already present on the user's computer system, | ||||
|     rather than copying library functions into the executable, and (2) | ||||
|     will operate properly with a modified version of the library, if | ||||
|     the user installs one, as long as the modified version is | ||||
|     interface-compatible with the version that the work was made with. | ||||
|  | ||||
|     c) Accompany the work with a written offer, valid for at | ||||
|     b) Accompany the work with a written offer, valid for at | ||||
|     least three years, to give the same user the materials | ||||
|     specified in Subsection 6a, above, for a charge no more | ||||
|     than the cost of performing this distribution. | ||||
|  | ||||
|     d) If distribution of the work is made by offering access to copy | ||||
|     c) If distribution of the work is made by offering access to copy | ||||
|     from a designated place, offer equivalent access to copy the above | ||||
|     specified materials from the same place. | ||||
|  | ||||
|     e) Verify that the user has already received a copy of these | ||||
|     d) Verify that the user has already received a copy of these | ||||
|     materials or that you have already sent this user a copy. | ||||
|  | ||||
|   For an executable, the required form of the "work that uses the | ||||
| Library" must include any data and utility programs needed for | ||||
| reproducing the executable from it.  However, as a special exception, | ||||
| the materials to be distributed need not include anything that is | ||||
| normally distributed (in either source or binary form) with the major | ||||
| the source code distributed need not include anything that is normally | ||||
| distributed (in either source or binary form) with the major | ||||
| components (compiler, kernel, and so on) of the operating system on | ||||
| which the executable runs, unless that component itself accompanies | ||||
| the executable. | ||||
| @@ -368,7 +348,7 @@ Library), the recipient automatically receives a license from the | ||||
| original licensor to copy, distribute, link with or modify the Library | ||||
| subject to these terms and conditions.  You may not impose any further | ||||
| restrictions on the recipients' exercise of the rights granted herein. | ||||
| You are not responsible for enforcing compliance by third parties with | ||||
| You are not responsible for enforcing compliance by third parties to | ||||
| this License. | ||||
|  | ||||
|   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. | ||||
|  | ||||
|   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, | ||||
| but may differ in detail to address new problems or concerns. | ||||
|  | ||||
| @@ -457,7 +437,7 @@ DAMAGES. | ||||
|  | ||||
| 		     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 | ||||
| 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> | ||||
|  | ||||
|     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 | ||||
|     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, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|     Lesser General Public License for more details. | ||||
|     Library General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU Lesser General Public | ||||
|     License along with this library; if not, write to the Free Software | ||||
|     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|     You should have received a copy of the GNU Library General Public | ||||
|     License along with this library; if not, write to the Free | ||||
|     Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | ||||
|     MA 02111-1307, USA | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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. | ||||
|  | ||||
|    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: | ||||
|      ./configure --with-lvm1=none  | ||||
|  | ||||
|    To separate the LVM1 support into a shared library loaded by lvm.conf use: | ||||
|      ./configure --with-lvm1=shared | ||||
|  | ||||
|    Use ./configure --help to see other options. | ||||
|  | ||||
| 2) Build and install. | ||||
| 3) Build and install LVM2. | ||||
|  | ||||
|    Run 'make' from the top directory to build everything you configured. | ||||
|    Run 'make install' to build and install everything you configured. | ||||
|    Run 'make install' from the top directory. | ||||
|  | ||||
|    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 | ||||
|    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  | ||||
| individual commands. | ||||
|   | ||||
							
								
								
									
										238
									
								
								Makefile.in
									
									
									
									
									
								
							
							
						
						
									
										238
									
								
								Makefile.in
									
									
									
									
									
								
							| @@ -1,235 +1,35 @@ | ||||
| # | ||||
| # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. | ||||
| # Copyright (C) 2001 Sistina Software | ||||
| # | ||||
| # 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, | ||||
| # modify, copy, or redistribute it subject to the terms and conditions | ||||
| # of the GNU General Public License v.2. | ||||
| # This LVM library is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
| # Library General Public License for more details. | ||||
| # | ||||
| # 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 | ||||
| # You should have received a copy of the GNU Library General Public | ||||
| # License along with this LVM library; if not, write to the Free | ||||
| # Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | ||||
| # MA 02111-1307, USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
| abs_top_builddir = @abs_top_builddir@ | ||||
| abs_top_srcdir = @abs_top_srcdir@ | ||||
| VPATH = @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) | ||||
|   SUBDIRS = conf include man test scripts \ | ||||
|     libdaemon lib tools daemons libdm \ | ||||
|     udev po liblvm python \ | ||||
|     unit-tests/datastruct unit-tests/mm unit-tests/regex | ||||
| tools.distclean: test.distclean | ||||
|   SUBDIRS += lib/format1 \ | ||||
| 	     test/mm test/device test/format1 test/regex test/filters | ||||
| endif | ||||
| DISTCLEAN_DIRS += lcov_reports* | ||||
| DISTCLEAN_TARGETS += config.cache config.log config.status make.tmpl | ||||
|  | ||||
| include make.tmpl | ||||
|  | ||||
| libdm: include | ||||
| libdaemon: include | ||||
| lib: libdm libdaemon | ||||
| liblvm: lib | ||||
| daemons: lib libdaemon tools | ||||
| tools: lib libdaemon device-mapper | ||||
| po: tools daemons | ||||
| scripts: liblvm libdm | ||||
| lib: include | ||||
| tools: include lib | ||||
|  | ||||
| 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 unit: all | ||||
| 	$(MAKE) -C test $(@) | ||||
|  | ||||
| conf.generate: tools | ||||
|  | ||||
| # how to use parenthesis in makefiles | ||||
| leftparen:=( | ||||
| LVM_VER := $(firstword $(subst $(leftparen), ,$(LVM_VERSION))) | ||||
| VER := LVM2.$(LVM_VER) | ||||
| # release file name | ||||
| FILE_VER := $(VER).tgz | ||||
| CLEAN_TARGETS += $(FILE_VER) | ||||
| CLEAN_DIRS += $(rpmbuilddir) | ||||
|  | ||||
| dist: | ||||
| 	@echo "Generating $(FILE_VER)";\ | ||||
| 	(cd $(top_srcdir); git ls-tree -r HEAD --name-only | xargs tar --transform "s,^,$(VER)/," -c) | gzip >$(FILE_VER) | ||||
|  | ||||
| rpm: dist | ||||
| 	$(RM) -r $(rpmbuilddir)/SOURCES | ||||
| 	$(MKDIR_P) $(rpmbuilddir)/SOURCES | ||||
| 	$(LN_S) -f $(abs_top_builddir)/$(FILE_VER) $(rpmbuilddir)/SOURCES | ||||
| 	$(LN_S) -f $(abs_top_srcdir)/spec/build.inc $(rpmbuilddir)/SOURCES | ||||
| 	$(LN_S) -f $(abs_top_srcdir)/spec/macros.inc $(rpmbuilddir)/SOURCES | ||||
| 	$(LN_S) -f $(abs_top_srcdir)/spec/packages.inc $(rpmbuilddir)/SOURCES | ||||
| 	DM_VER=$$(cut -d- -f1 $(top_srcdir)/VERSION_DM);\ | ||||
| 	GIT_VER=$$(cd $(top_srcdir); git describe | cut -d- --output-delimiter=. -f2,3 || echo 0);\ | ||||
| 	sed -e "s,\(device_mapper_version\) [0-9.]*$$,\1 $$DM_VER," \ | ||||
| 	    -e "s,^\(Version:[^0-9%]*\)[0-9.]*$$,\1 $(LVM_VER)," \ | ||||
| 	    -e "s,^\(Release:[^0-9%]*\)[0-9.]\+,\1 $$GIT_VER," \ | ||||
| 	    $(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc | ||||
| 	rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec | ||||
|  | ||||
| generate: conf.generate | ||||
| 	$(MAKE) -C conf generate | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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 | ||||
| all: tags | ||||
| tags: | ||||
| 	test -z "$(shell find $(top_srcdir) -type f -name '*.[ch]' -newer tags | head -1)" || $(RM) tags | ||||
| 	test -f tags || find $(top_srcdir) -maxdepth 4 -type f -name '*.[ch]' -exec ctags -a '{}' + | ||||
|  | ||||
| DISTCLEAN_TARGETS += tags | ||||
| endif | ||||
|   | ||||
							
								
								
									
										40
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								README
									
									
									
									
									
								
							| @@ -1,33 +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. | ||||
|  | ||||
| This is beta-quality software, released for testing purposes only. | ||||
| There is no warranty - see COPYING and COPYING.LIB. | ||||
|  | ||||
| Tarballs are available from: | ||||
|   ftp://sources.redhat.com/pub/lvm2/ | ||||
|   ftp://ftp.sistina.com/pub/LVM2/tools/ | ||||
|   ftp://ftp.sistina.com/pub/LVM2/device-mapper/ | ||||
|  | ||||
| The source code is stored in git: | ||||
|   http://git.fedorahosted.org/git/lvm2.git | ||||
|   git clone git://git.fedorahosted.org/git/lvm2.git | ||||
| To access the CVS tree use: | ||||
|   cvs -d :pserver:cvs@tech.sistina.com:/data/cvs login | ||||
|   CVS password: cvs1 | ||||
|   cvs -d :pserver:cvs@tech.sistina.com:/data/cvs checkout LVM2 | ||||
|  | ||||
| Mailing list for general discussion related to LVM2: | ||||
|   linux-lvm@redhat.com | ||||
|   Subscribe from https://www.redhat.com/mailman/listinfo/linux-lvm | ||||
|  | ||||
| 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. | ||||
| Mailing list for discussion/bug reports etc. | ||||
|   lvm-devel@sistina.com | ||||
|   Subscribe from http://lists.sistina.com/mailman/listinfo/lvm-devel | ||||
|  | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| 1.02.105-git (2015-08-10) | ||||
							
								
								
									
										1083
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							
							
						
						
									
										1083
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										63
									
								
								acinclude.m4
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								acinclude.m4
									
									
									
									
									
								
							| @@ -1,63 +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 | ||||
| ]) | ||||
							
								
								
									
										230
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										230
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							| @@ -1,230 +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($@)])]) | ||||
| # 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 | ||||
|  | ||||
| m4_include([acinclude.m4]) | ||||
							
								
								
									
										1652
									
								
								autoconf/config.guess
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1652
									
								
								autoconf/config.guess
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										911
									
								
								autoconf/config.sub
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										911
									
								
								autoconf/config.sub
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,62 +1,36 @@ | ||||
| #!/bin/sh | ||||
| # | ||||
| # install - install a program, script, or datafile | ||||
|  | ||||
| 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. | ||||
| # This comes from X11R5 (mit/util/scripts/install.sh). | ||||
| # | ||||
| # 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 | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | ||||
| # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | ||||
| # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- | ||||
| # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
| # | ||||
| # Except as contained in this notice, the name of the X Consortium shall not | ||||
| # be used in advertising or otherwise to promote the sale, use or other deal- | ||||
| # ings in this Software without prior written authorization from the X Consor- | ||||
| # tium. | ||||
| # | ||||
| # | ||||
| # FSF changes to this file are in the public domain. | ||||
| # Permission to use, copy, modify, distribute, and sell this software and its | ||||
| # documentation for any purpose is hereby granted without fee, provided that | ||||
| # the above copyright notice appear in all copies and that both that | ||||
| # copyright notice and this permission notice appear in supporting | ||||
| # documentation, and that the name of M.I.T. not be used in advertising or | ||||
| # publicity pertaining to distribution of the software without specific, | ||||
| # written prior permission.  M.I.T. makes no representations about the | ||||
| # suitability of this software for any purpose.  It is provided "as is" | ||||
| # without express or implied warranty. | ||||
| # | ||||
| # Calling this script install-sh is preferred over install.sh, to prevent | ||||
| # `make' implicit rules from creating a file called install from it | ||||
| # when there is no Makefile. | ||||
| # | ||||
| # This script is compatible with the BSD install script, but was written | ||||
| # from scratch. | ||||
| # 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 | ||||
|  | ||||
| # Don't use :- since 4.3BSD and earlier shells don't like it. | ||||
| 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}" | ||||
| cpprog="${CPPROG-cp}" | ||||
| @@ -67,441 +41,211 @@ stripprog="${STRIPPROG-strip}" | ||||
| rmprog="${RMPROG-rm}" | ||||
| mkdirprog="${MKDIRPROG-mkdir}" | ||||
|  | ||||
| posix_glob= | ||||
| posix_mkdir= | ||||
|  | ||||
| # Desired mode of installed file. | ||||
| mode=0755 | ||||
|  | ||||
| chmodcmd=$chmodprog | ||||
| chowncmd= | ||||
| chgrpcmd= | ||||
| stripcmd= | ||||
| transformbasename="" | ||||
| transform_arg="" | ||||
| instcmd="$mvprog" | ||||
| chmodcmd="$chmodprog 0755" | ||||
| chowncmd="" | ||||
| chgrpcmd="" | ||||
| stripcmd="" | ||||
| rmcmd="$rmprog -f" | ||||
| mvcmd="$mvprog" | ||||
| src= | ||||
| dst= | ||||
| dir_arg= | ||||
| dstarg= | ||||
| no_target_directory= | ||||
| src="" | ||||
| dst="" | ||||
| dir_arg="" | ||||
|  | ||||
| usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE | ||||
|    or: $0 [OPTION]... SRCFILES... DIRECTORY | ||||
|    or: $0 [OPTION]... -t DIRECTORY SRCFILES... | ||||
|    or: $0 [OPTION]... -d DIRECTORIES... | ||||
| while [ x"$1" != x ]; do | ||||
|     case $1 in | ||||
| 	-c) instcmd="$cpprog" | ||||
| 	    shift | ||||
| 	    continue;; | ||||
|  | ||||
| In the 1st form, copy SRCFILE to DSTFILE. | ||||
| In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. | ||||
| In the 4th, create DIRECTORIES. | ||||
| 	-d) dir_arg=true | ||||
| 	    shift | ||||
| 	    continue;; | ||||
|  | ||||
| Options: | ||||
| -c         (ignored) | ||||
| -d         create directories instead of installing files. | ||||
| -g GROUP   $chgrpprog installed files to GROUP. | ||||
| -m MODE    $chmodprog installed files to MODE. | ||||
| -o USER    $chownprog installed files to USER. | ||||
| -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. | ||||
| 	-m) chmodcmd="$chmodprog $2" | ||||
| 	    shift | ||||
| 	    shift | ||||
| 	    continue;; | ||||
|  | ||||
| Environment variables override the default commands: | ||||
|   CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG | ||||
| " | ||||
| 	-o) chowncmd="$chownprog $2" | ||||
| 	    shift | ||||
| 	    shift | ||||
| 	    continue;; | ||||
|  | ||||
| while test $# -ne 0; do | ||||
|   case $1 in | ||||
|     -c) shift | ||||
|         continue;; | ||||
| 	-g) chgrpcmd="$chgrpprog $2" | ||||
| 	    shift | ||||
| 	    shift | ||||
| 	    continue;; | ||||
|  | ||||
|     -d) dir_arg=true | ||||
|         shift | ||||
|         continue;; | ||||
| 	-s) stripcmd="$stripprog" | ||||
| 	    shift | ||||
| 	    continue;; | ||||
|  | ||||
|     -g) chgrpcmd="$chgrpprog $2" | ||||
|         shift | ||||
|         shift | ||||
|         continue;; | ||||
| 	-t=*) transformarg=`echo $1 | sed 's/-t=//'` | ||||
| 	    shift | ||||
| 	    continue;; | ||||
|  | ||||
|     --help) echo "$usage"; exit $?;; | ||||
| 	-b=*) transformbasename=`echo $1 | sed 's/-b=//'` | ||||
| 	    shift | ||||
| 	    continue;; | ||||
|  | ||||
|     -m) mode=$2 | ||||
|         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 | ||||
| 	*)  if [ x"$src" = x ] | ||||
| 	    then | ||||
| 	      if test -z "$dir_arg" || { | ||||
| 		   # Check for POSIX incompatibilities with -m. | ||||
| 		   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or | ||||
| 		   # other-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" | ||||
| 		src=$1 | ||||
| 	    else | ||||
| 	      # Remove any dirs left behind by ancient mkdir implementations. | ||||
| 	      rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null | ||||
| 		# this colon is to work around a 386BSD /bin/sh bug | ||||
| 		: | ||||
| 		dst=$1 | ||||
| 	    fi | ||||
| 	    trap '' 0;; | ||||
| 	esac;; | ||||
| 	    shift | ||||
| 	    continue;; | ||||
|     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 | ||||
|  | ||||
| # Local variables: | ||||
| # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||
| # time-stamp-start: "scriptversion=" | ||||
| # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||
| # time-stamp-end: "$" | ||||
| # End: | ||||
| if [ x"$src" = x ] | ||||
| then | ||||
| 	echo "install:	no input file specified" | ||||
| 	exit 1 | ||||
| else | ||||
| 	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 | ||||
|   | ||||
							
								
								
									
										4
									
								
								conf/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								conf/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +0,0 @@ | ||||
| command_profile_template.profile | ||||
| example.conf | ||||
| lvmlocal.conf | ||||
| metadata_profile_template.profile | ||||
| @@ -1,57 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| .PHONY: install_conf install_localconf install_profiles | ||||
|  | ||||
| generate: | ||||
| 	(cat $(top_srcdir)/conf/example.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --ignorelocal --withspaces) > example.conf.in | ||||
| 	(cat $(top_srcdir)/conf/lvmlocal.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --withspaces local) > lvmlocal.conf.in | ||||
|  | ||||
| install_conf: $(CONFSRC) | ||||
| 	@if [ ! -e $(confdir)/$(CONFDEST) ]; then \ | ||||
| 		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) $(DESTDIR)$(DEFAULT_PROFILE_DIR) | ||||
| 	$(INSTALL_DATA) $(PROFILES) $(DESTDIR)$(DEFAULT_PROFILE_DIR)/ | ||||
|  | ||||
| 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 settins for "smq" policy | ||||
| 	} | ||||
| } | ||||
| @@ -1,49 +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. | ||||
| # | ||||
| global { | ||||
| 	units="h" | ||||
| 	si_unit_consistency=1 | ||||
| 	suffix=1 | ||||
| 	lvdisplay_shows_full_device_path=0 | ||||
| } | ||||
| report { | ||||
| 	compact_output=0 | ||||
| 	aligned=1 | ||||
| 	buffered=1 | ||||
| 	headings=1 | ||||
| 	separator=" " | ||||
| 	list_item_separator="," | ||||
| 	prefixes=0 | ||||
| 	quoted=1 | ||||
| 	colums_as_rows=0 | ||||
| 	binary_values_as_numeric=0 | ||||
| 	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" | ||||
| } | ||||
| @@ -1,23 +0,0 @@ | ||||
| # This is an example configuration file for the LVM2 system. | ||||
| # It contains the default settings that would be used if there was no | ||||
| # @DEFAULT_SYS_DIR@/lvm.conf file. | ||||
| # | ||||
| # Refer to 'man lvm.conf' for further information including the file layout. | ||||
| # | ||||
| # Refer to 'man lvm.conf' for information about how settings configured in | ||||
| # this file are combined with built-in values and command line options to | ||||
| # arrive at the final values used by LVM. | ||||
| # | ||||
| # Refer to 'man lvmconfig' for information about displaying the built-in | ||||
| # and configured values used by LVM. | ||||
| # | ||||
| # If a default value is set in this file (not commented out), then a | ||||
| # new version of LVM using this file will continue using that value, | ||||
| # even if the new version of LVM changes the built-in default value. | ||||
| # | ||||
| # To put this file in a different directory and override @DEFAULT_SYS_DIR@ set | ||||
| # the environment variable LVM_SYSTEM_DIR before running the tools. | ||||
| # | ||||
| # N.B. Take care that each setting only appears once if uncommenting | ||||
| # example settings in this file. | ||||
|  | ||||
							
								
								
									
										1679
									
								
								conf/example.conf.in
									
									
									
									
									
								
							
							
						
						
									
										1679
									
								
								conf/example.conf.in
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,19 +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. | ||||
|  | ||||
| @@ -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 = "" | ||||
| 	# Example: | ||||
| 	# Set the system_id to the string 'host1'. | ||||
| 	# system_id = "host1" | ||||
| 	# 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 a unique among all hosts, | ||||
| 	# and must be between 1 and 2000. | ||||
| 	# 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 | ||||
| } | ||||
							
								
								
									
										2211
									
								
								configure.in
									
									
									
									
									
								
							
							
						
						
									
										2211
									
								
								configure.in
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,55 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 ($(MAKECMDGOALS),distclean) | ||||
|   SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd | ||||
| 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,103 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 | ||||
|  | ||||
| LVMLIBS = $(LVMINTERNAL_LIBS) | ||||
|  | ||||
| ifeq ("@DMEVENTD@", "yes") | ||||
| 	LVMLIBS += -ldevmapper-event | ||||
| endif | ||||
|   | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LVMLIBS += -ldevmapper | ||||
| LIBS += $(PTHREAD_LIBS) | ||||
|  | ||||
| CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS) | ||||
| LDFLAGS += $(EXTRA_EXEC_LDFLAGS) | ||||
|  | ||||
| INSTALL_TARGETS = \ | ||||
| 	install_clvmd | ||||
|  | ||||
| clvmd: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o clvmd $(OBJECTS) \ | ||||
| 		$(LVMLIBS) $(LMLIBS) $(LIBS) | ||||
|  | ||||
| .PHONY: install_clvmd | ||||
|  | ||||
| install_clvmd: $(TARGETS) | ||||
| 	$(INSTALL_PROGRAM) -D clvmd $(usrsbindir)/clvmd | ||||
|  | ||||
| install: $(INSTALL_TARGETS) | ||||
|  | ||||
| install_cluster: $(INSTALL_TARGETS) | ||||
| @@ -1,83 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 */ | ||||
| static const char 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.*/ | ||||
| #define NODE_ALL	"*" | ||||
| #define NODE_LOCAL	"." | ||||
| #define NODE_REMOTE	"^" | ||||
|  | ||||
| #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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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("doing PRE command LOCK_VG '%s' at %x (client=%p)\n", lockname, lock_cmd, client); | ||||
|  | ||||
| 	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("Client thread cleanup (%p)\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("Cleanup (%p): 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,658 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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) | ||||
| { | ||||
| 	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; | ||||
|  | ||||
| 	err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2); | ||||
| 	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,689 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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; | ||||
|  | ||||
| 	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; | ||||
| 	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,932 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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; | ||||
| 		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_label_scan(cmd, 2); | ||||
| 	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 (!(cmd = create_toolcontext(1, NULL, 0, 1, 1, 1))) { | ||||
| 		log_error("Failed to allocate command 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); | ||||
| 		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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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) | ||||
|  | ||||
| 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,256 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #include "logging.h" | ||||
| #include "common.h" | ||||
| #include "functions.h" | ||||
| #include "link_mon.h" | ||||
| #include "local.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); | ||||
|  | ||||
| int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) | ||||
| { | ||||
| 	daemonize(); | ||||
|  | ||||
| 	init_all(); | ||||
|  | ||||
| 	/* Parent can now exit, we're ready to handle requests */ | ||||
| 	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); | ||||
|  | ||||
| 	(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; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * init_all | ||||
|  * | ||||
|  * Initialize modules.  Exit on failure. | ||||
|  */ | ||||
| static void init_all(void) | ||||
| { | ||||
| 	int r; | ||||
|  | ||||
| 	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,110 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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) | ||||
|  | ||||
| LIBS += -ldevmapper | ||||
| LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS) | ||||
|  | ||||
| CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS) | ||||
|  | ||||
| dmeventd: $(LIB_SHARED) dmeventd.o | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) -L. -o $@ dmeventd.o \ | ||||
| 	$(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic | ||||
|  | ||||
| dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \ | ||||
| 	dmeventd.o $(DL_LIBS) $(LVMLIBS) $(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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,870 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "dmlib.h" | ||||
| #include "libdevmapper-event.h" | ||||
| //#include "libmultilog.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 */ | ||||
|  | ||||
| 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; | ||||
| 			else { | ||||
| 				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; | ||||
| 			else { | ||||
| 				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 }; | ||||
|  | ||||
| 	if (stat(fifos->client_path, &statbuf)) | ||||
| 		goto start_server; | ||||
|  | ||||
| 	if (!S_ISFIFO(statbuf.st_mode)) { | ||||
| 		log_error("%s is not a fifo.", 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) { | ||||
| 		/* server is running and listening */ | ||||
| 		if (close(fifos->client)) | ||||
| 			log_sys_debug("close", fifos->client_path); | ||||
| 		return 1; | ||||
| 	} else if (errno != ENXIO) { | ||||
| 		/* 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)) { | ||||
| 		stack; | ||||
| 		return -ESRCH; | ||||
| 	} | ||||
|  | ||||
| 	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); | ||||
|  | ||||
| 	/* 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); | ||||
|  | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| #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'\n", | ||||
| 				  msg.data); | ||||
| 			dm_free(msg.data); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 		*timeout = atoi(p); | ||||
| 	} | ||||
| 	dm_free(msg.data); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
| #endif | ||||
| @@ -1,116 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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); | ||||
|  | ||||
| /* 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,177 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "log.h" | ||||
|  | ||||
| #include "lvm2cmd.h" | ||||
| #include "dmeventd_lvm.h" | ||||
|  | ||||
| #include <pthread.h> | ||||
| #include <syslog.h> | ||||
|  | ||||
| extern int dmeventd_debug; | ||||
|  | ||||
| /* | ||||
|  * 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; | ||||
|  | ||||
| /* | ||||
|  * Currently only one event can be processed at a time. | ||||
|  */ | ||||
| static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER; | ||||
|  | ||||
| /* | ||||
|  * FIXME Do not pass things directly to syslog, rather use the existing logging | ||||
|  * facilities to sort logging ... however that mechanism needs to be somehow | ||||
|  * configurable and we don't have that option yet | ||||
|  */ | ||||
| static void _temporary_log_fn(int level, | ||||
| 			      const char *file __attribute__((unused)), | ||||
| 			      int line __attribute__((unused)), | ||||
| 			      int dm_errno __attribute__((unused)), | ||||
| 			      const char *message) | ||||
| { | ||||
| 	level &= ~(_LOG_STDERR | _LOG_ONCE); | ||||
|  | ||||
| 	switch (level) { | ||||
| 	case _LOG_DEBUG: | ||||
| 		if (dmeventd_debug >= 3) | ||||
| 			syslog(LOG_DEBUG, "%s", message); | ||||
| 		break; | ||||
| 	case _LOG_INFO: | ||||
| 		if (dmeventd_debug >= 2) | ||||
| 			syslog(LOG_INFO, "%s", message); | ||||
| 		break; | ||||
| 	case _LOG_NOTICE: | ||||
| 		if (dmeventd_debug >= 1) | ||||
| 			syslog(LOG_NOTICE, "%s", message); | ||||
| 		break; | ||||
| 	case _LOG_WARN: | ||||
| 		syslog(LOG_WARNING, "%s", message); | ||||
| 		break; | ||||
| 	case _LOG_ERR: | ||||
| 		syslog(LOG_ERR, "%s", message); | ||||
| 		break; | ||||
| 	default: | ||||
| 		syslog(LOG_CRIT, "%s", message); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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); | ||||
|  | ||||
| 	/* | ||||
| 	 * 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))) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!_lvm_handle) { | ||||
| 		if (!getenv("LVM_LOG_FILE_EPOCH")) | ||||
| 			lvm2_log_fn(_temporary_log_fn); | ||||
| 		if (!(_lvm_handle = lvm2_init())) { | ||||
| 			dm_pool_destroy(_mem_pool); | ||||
| 			_mem_pool = NULL; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		lvm2_disable_dmeventd_monitoring(_lvm_handle); | ||||
| 		/* FIXME Temporary: move to dmeventd core */ | ||||
| 		lvm2_run(_lvm_handle, "_memlock_inc"); | ||||
| 	} | ||||
|  | ||||
| 	_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) { | ||||
| 		lvm2_run(_lvm_handle, "_memlock_dec"); | ||||
| 		dm_pool_destroy(_mem_pool); | ||||
| 		_mem_pool = NULL; | ||||
| 		lvm2_exit(_lvm_handle); | ||||
| 		_lvm_handle = NULL; | ||||
| 	} | ||||
|  | ||||
| 	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) | ||||
| { | ||||
| 	char *vg = NULL, *lv = NULL, *layer; | ||||
| 	int r; | ||||
|  | ||||
| 	if (!dm_split_lvm_name(mem, device, &vg, &lv, &layer)) { | ||||
| 		syslog(LOG_ERR, "Unable to determine VG name from %s.\n", | ||||
| 		       device); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* strip off the mirror component designations */ | ||||
| 	if ((layer = strstr(lv, "_mimagetmp")) || | ||||
| 	    (layer = strstr(lv, "_mlog"))) | ||||
| 		*layer = '\0'; | ||||
|  | ||||
| 	r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv); | ||||
|  | ||||
| 	dm_pool_free(mem, vg); | ||||
|  | ||||
| 	if (r < 0) { | ||||
| 		syslog(LOG_ERR, "Unable to form LVM command. (too long).\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
| @@ -1,42 +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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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); | ||||
|  | ||||
| #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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,243 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2005-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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
|  | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "defaults.h" | ||||
|  | ||||
| #include <syslog.h> /* FIXME Replace syslog with multilog */ | ||||
| /* FIXME Missing openlog? */ | ||||
| /* FIXME Replace most syslogs with log_error() style messages and add complete context. */ | ||||
| /* FIXME Reformat to 80 char lines. */ | ||||
|  | ||||
| #define ME_IGNORE    0 | ||||
| #define ME_INSYNC    1 | ||||
| #define ME_FAILURE   2 | ||||
|  | ||||
| static int _process_status_code(const char status_code, const char *dev_name, | ||||
| 				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) | ||||
| 	 */  | ||||
| 	if (status_code == 'F') { | ||||
| 		syslog(LOG_ERR, "%s device %s flush failed.", | ||||
| 		       dev_type, dev_name); | ||||
| 		r = ME_FAILURE; | ||||
| 	} else if (status_code == 'S') | ||||
| 		syslog(LOG_ERR, "%s device %s sync failed.", | ||||
| 		       dev_type, dev_name); | ||||
| 	else if (status_code == 'R') | ||||
| 		syslog(LOG_ERR, "%s device %s read failed.", | ||||
| 		       dev_type, dev_name); | ||||
| 	else if (status_code != 'A') { | ||||
| 		syslog(LOG_ERR, "%s device %s has failed (%c).", | ||||
| 		       dev_type, dev_name, status_code); | ||||
| 		r = ME_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _get_mirror_event(char *params) | ||||
| { | ||||
| 	int i, r = ME_INSYNC; | ||||
| 	char **args = NULL; | ||||
| 	char *dev_status_str; | ||||
| 	char *log_status_str; | ||||
| 	char *sync_str; | ||||
| 	char *p = NULL; | ||||
| 	int log_argc, num_devs; | ||||
|  | ||||
| 	/* | ||||
| 	 * dm core parms:	     0 409600 mirror | ||||
| 	 * Mirror core parms:	     2 253:4 253:5 400/400 | ||||
| 	 * New-style failure params: 1 AA | ||||
| 	 * New-style log params:     3 cluster 253:3 A | ||||
| 	 *			 or  3 disk 253:3 A | ||||
| 	 *			 or  1 core | ||||
| 	 */ | ||||
|  | ||||
| 	/* number of devices */ | ||||
| 	if (!dm_split_words(params, 1, 0, &p)) | ||||
| 		goto out_parse; | ||||
|  | ||||
| 	if (!(num_devs = atoi(p)) || | ||||
| 	    (num_devs > DEFAULT_MIRROR_MAX_IMAGES) || (num_devs < 0)) | ||||
| 		goto out_parse; | ||||
| 	p += strlen(p) + 1; | ||||
|  | ||||
| 	/* devices names + "400/400" + "1 AA" + 1 or 3 log parms + NULL */ | ||||
| 	args = dm_malloc((num_devs + 7) * sizeof(char *)); | ||||
| 	if (!args || dm_split_words(p, num_devs + 7, 0, args) < num_devs + 5) | ||||
| 		goto out_parse; | ||||
|  | ||||
| 	/* FIXME: Code differs from lib/mirror/mirrored.c */ | ||||
| 	dev_status_str = args[2 + num_devs]; | ||||
| 	log_argc = atoi(args[3 + num_devs]); | ||||
| 	log_status_str = args[3 + num_devs + log_argc]; | ||||
| 	sync_str = args[num_devs]; | ||||
|  | ||||
| 	/* Check for bad mirror devices */ | ||||
| 	for (i = 0; i < num_devs; i++) | ||||
| 		r = _process_status_code(dev_status_str[i], args[i], | ||||
| 			i ? "Secondary mirror" : "Primary mirror", r); | ||||
|  | ||||
| 	/* Check for bad disk log device */ | ||||
| 	if (log_argc > 1) | ||||
| 		r = _process_status_code(log_status_str[0], | ||||
| 					 args[2 + num_devs + log_argc], | ||||
| 					 "Log", r); | ||||
|  | ||||
| 	if (r == ME_FAILURE) | ||||
| 		goto out; | ||||
|  | ||||
| 	p = strstr(sync_str, "/"); | ||||
| 	if (p) { | ||||
| 		p[0] = '\0'; | ||||
| 		if (strcmp(sync_str, p+1)) | ||||
| 			r = ME_IGNORE; | ||||
| 		p[0] = '/'; | ||||
| 	} else | ||||
| 		goto out_parse; | ||||
|  | ||||
| out: | ||||
| 	dm_free(args); | ||||
| 	return r; | ||||
|  | ||||
| out_parse: | ||||
| 	dm_free(args); | ||||
| 	syslog(LOG_ERR, "Unable to parse mirror status string."); | ||||
| 	return ME_IGNORE; | ||||
| } | ||||
|  | ||||
| static int _remove_failed_devices(const char *device) | ||||
| { | ||||
| 	int r; | ||||
| #define CMD_SIZE 256	/* FIXME Use system restriction */ | ||||
| 	char cmd_str[CMD_SIZE]; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str), | ||||
| 				   "lvscan --cache", device)) | ||||
| 		return -1; | ||||
|  | ||||
| 	r = dmeventd_lvm2_run(cmd_str); | ||||
|  | ||||
| 	if (!r) | ||||
| 		syslog(LOG_INFO, "Re-scan of mirror device %s failed.", device); | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str), | ||||
| 				  "lvconvert --config devices{ignore_suspended_devices=1} " | ||||
| 				  "--repair --use-policies", device)) | ||||
| 		return -ENAMETOOLONG; /* FIXME Replace with generic error return - reason for failure has already got logged */ | ||||
|  | ||||
| 	/* if repair goes OK, report success even if lvscan has failed */ | ||||
| 	r = dmeventd_lvm2_run(cmd_str); | ||||
|  | ||||
| 	syslog(LOG_INFO, "Repair of mirrored device %s %s.", device, | ||||
| 	       (r) ? "finished successfully" : "failed"); | ||||
|  | ||||
| 	return (r) ? 0 : -1; | ||||
| } | ||||
|  | ||||
| void process_event(struct dm_task *dmt, | ||||
| 		   enum dm_event_mask event __attribute__((unused)), | ||||
| 		   void **unused __attribute__((unused))) | ||||
| { | ||||
| 	void *next = NULL; | ||||
| 	uint64_t start, length; | ||||
| 	char *target_type = NULL; | ||||
| 	char *params; | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
|  | ||||
| 	dmeventd_lvm2_lock(); | ||||
|  | ||||
| 	do { | ||||
| 		next = dm_get_next_target(dmt, next, &start, &length, | ||||
| 					  &target_type, ¶ms); | ||||
|  | ||||
| 		if (!target_type) { | ||||
| 			syslog(LOG_INFO, "%s mapping lost.", device); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (strcmp(target_type, "mirror")) { | ||||
| 			syslog(LOG_INFO, "%s has unmirrored portion.", device); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		switch(_get_mirror_event(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 | ||||
| 			*/ | ||||
| 			syslog(LOG_NOTICE, "%s is now in-sync.", device); | ||||
| 			break; | ||||
| 		case ME_FAILURE: | ||||
| 			syslog(LOG_ERR, "Device failure in %s.", device); | ||||
| 			if (_remove_failed_devices(device)) | ||||
| 				/* FIXME Why are all the error return codes unused? Get rid of them? */ | ||||
| 				syslog(LOG_ERR, "Failed to remove faulty devices in %s.", | ||||
| 				       device); | ||||
| 			/* Should check before warning user that device is now linear | ||||
| 			else | ||||
| 				syslog(LOG_NOTICE, "%s is now a linear device.\n", | ||||
| 					device); | ||||
| 			*/ | ||||
| 			break; | ||||
| 		case ME_IGNORE: | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* FIXME Provide value then! */ | ||||
| 			syslog(LOG_INFO, "Unknown event received."); | ||||
| 		} | ||||
| 	} while (next); | ||||
|  | ||||
| 	dmeventd_lvm2_unlock(); | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| 		    const char *uuid __attribute__((unused)), | ||||
| 		    int major __attribute__((unused)), | ||||
| 		    int minor __attribute__((unused)), | ||||
| 		    void **unused __attribute__((unused))) | ||||
| { | ||||
| 	if (!dmeventd_lvm2_init()) | ||||
| 		return 0; | ||||
|  | ||||
| 	syslog(LOG_INFO, "Monitoring mirror device %s for events.", device); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int unregister_device(const char *device, | ||||
| 		      const char *uuid __attribute__((unused)), | ||||
| 		      int major __attribute__((unused)), | ||||
| 		      int minor __attribute__((unused)), | ||||
| 		      void **unused __attribute__((unused))) | ||||
| { | ||||
| 	syslog(LOG_INFO, "No longer monitoring mirror device %s for events.", | ||||
| 	       device); | ||||
| 	dmeventd_lvm2_exit(); | ||||
|  | ||||
| 	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,180 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 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 Lesser General Public License v.2.1. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with this program; if not, write to the Free Software Foundation, | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
|  | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd_lvm.h" | ||||
|  | ||||
| #include <syslog.h> /* FIXME Replace syslog with multilog */ | ||||
| /* FIXME Missing openlog? */ | ||||
| /* FIXME Replace most syslogs with log_error() style messages and add complete context. */ | ||||
| /* FIXME Reformat to 80 char lines. */ | ||||
|  | ||||
| /* | ||||
|  * run_repair is a close copy to | ||||
|  * plugins/mirror/dmeventd_mirror.c:_remove_failed_devices() | ||||
|  */ | ||||
| static int run_repair(const char *device) | ||||
| { | ||||
| 	int r; | ||||
| #define CMD_SIZE 256	/* FIXME Use system restriction */ | ||||
| 	char cmd_str[CMD_SIZE]; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str), | ||||
| 				  "lvscan --cache", device)) | ||||
| 		return -1; | ||||
|  | ||||
| 	r = dmeventd_lvm2_run(cmd_str); | ||||
|  | ||||
| 	if (!r) | ||||
| 		syslog(LOG_INFO, "Re-scan of RAID device %s failed.", device); | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str), | ||||
| 				  "lvconvert --config devices{ignore_suspended_devices=1} " | ||||
| 				  "--repair --use-policies", device)) | ||||
| 		return -1; | ||||
|  | ||||
| 	/* if repair goes OK, report success even if lvscan has failed */ | ||||
| 	r = dmeventd_lvm2_run(cmd_str); | ||||
|  | ||||
| 	if (!r) | ||||
| 		syslog(LOG_INFO, "Repair of RAID device %s failed.", device); | ||||
|  | ||||
| 	return (r) ? 0 : -1; | ||||
| } | ||||
|  | ||||
| static int _process_raid_event(char *params, const char *device) | ||||
| { | ||||
| 	int i, n, failure = 0; | ||||
| 	char *p, *a[4]; | ||||
| 	char *raid_type; | ||||
| 	char *num_devices; | ||||
| 	char *health_chars; | ||||
| 	char *resync_ratio; | ||||
|  | ||||
| 	/* | ||||
| 	 * RAID parms:     <raid_type> <#raid_disks> \ | ||||
| 	 *                 <health chars> <resync ratio> | ||||
| 	 */ | ||||
| 	if (!dm_split_words(params, 4, 0, a)) { | ||||
| 		syslog(LOG_ERR, "Failed to process status line for %s\n", | ||||
| 		       device); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	raid_type = a[0]; | ||||
| 	num_devices = a[1]; | ||||
| 	health_chars = a[2]; | ||||
| 	resync_ratio = a[3]; | ||||
|  | ||||
| 	if (!(n = atoi(num_devices))) { | ||||
| 		syslog(LOG_ERR, "Failed to parse number of devices for %s: %s", | ||||
| 		       device, num_devices); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; i < n; i++) { | ||||
| 		switch (health_chars[i]) { | ||||
| 		case 'A': | ||||
| 			/* Device is 'A'live and well */ | ||||
| 		case 'a': | ||||
| 			/* Device is 'a'live, but not yet in-sync */ | ||||
| 			break; | ||||
| 		case 'D': | ||||
| 			syslog(LOG_ERR, | ||||
| 			       "Device #%d of %s array, %s, has failed.", | ||||
| 			       i, raid_type, device); | ||||
| 			failure++; | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* Unhandled character returned from kernel */ | ||||
| 			break; | ||||
| 		} | ||||
| 		if (failure) | ||||
| 			return run_repair(device); | ||||
| 	} | ||||
|  | ||||
| 	p = strstr(resync_ratio, "/"); | ||||
| 	if (!p) { | ||||
| 		syslog(LOG_ERR, "Failed to parse resync_ratio for %s: %s", | ||||
| 		       device, resync_ratio); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	p[0] = '\0'; | ||||
| 	syslog(LOG_INFO, "%s array, %s, is %s in-sync.", | ||||
| 	       raid_type, device, strcmp(resync_ratio, p+1) ? "not" : "now"); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void process_event(struct dm_task *dmt, | ||||
| 		   enum dm_event_mask event __attribute__((unused)), | ||||
| 		   void **unused __attribute__((unused))) | ||||
| { | ||||
| 	void *next = NULL; | ||||
| 	uint64_t start, length; | ||||
| 	char *target_type = NULL; | ||||
| 	char *params; | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
|  | ||||
| 	dmeventd_lvm2_lock(); | ||||
|  | ||||
| 	do { | ||||
| 		next = dm_get_next_target(dmt, next, &start, &length, | ||||
| 					  &target_type, ¶ms); | ||||
|  | ||||
| 		if (!target_type) { | ||||
| 			syslog(LOG_INFO, "%s mapping lost.", device); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (strcmp(target_type, "raid")) { | ||||
| 			syslog(LOG_INFO, "%s has non-raid portion.", device); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (_process_raid_event(params, device)) | ||||
| 			syslog(LOG_ERR, "Failed to process event for %s", | ||||
| 			       device); | ||||
| 	} while (next); | ||||
|  | ||||
| 	dmeventd_lvm2_unlock(); | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| 		    const char *uuid __attribute__((unused)), | ||||
| 		    int major __attribute__((unused)), | ||||
| 		    int minor __attribute__((unused)), | ||||
| 		    void **unused __attribute__((unused))) | ||||
| { | ||||
| 	if (!dmeventd_lvm2_init()) | ||||
| 		return 0; | ||||
|  | ||||
| 	syslog(LOG_INFO, "Monitoring RAID device %s for events.", device); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int unregister_device(const char *device, | ||||
| 		      const char *uuid __attribute__((unused)), | ||||
| 		      int major __attribute__((unused)), | ||||
| 		      int minor __attribute__((unused)), | ||||
| 		      void **unused __attribute__((unused))) | ||||
| { | ||||
| 	syslog(LOG_INFO, "No longer monitoring RAID device %s for events.", | ||||
| 	       device); | ||||
| 	dmeventd_lvm2_exit(); | ||||
|  | ||||
| 	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,247 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2007-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 Lesser General Public License v.2.1. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with this program; if not, write to the Free Software Foundation, | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
|  | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd_lvm.h" | ||||
|  | ||||
| #include <sys/wait.h> | ||||
| #include <syslog.h> /* FIXME Replace syslog with multilog */ | ||||
| #include <stdarg.h> | ||||
| /* FIXME Missing openlog? */ | ||||
|  | ||||
| /* First warning when snapshot is 80% full. */ | ||||
| #define WARNING_THRESH 80 | ||||
| /* Run a check every 5%. */ | ||||
| #define CHECK_STEP 5 | ||||
| /* Do not bother checking snapshots less than 50% full. */ | ||||
| #define CHECK_MINIMUM 50 | ||||
|  | ||||
| #define UMOUNT_COMMAND "/bin/umount" | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	int percent_check; | ||||
| 	uint64_t known_size; | ||||
| 	char cmd_str[1024]; | ||||
| }; | ||||
|  | ||||
| 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); | ||||
|                 syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno)); | ||||
|                 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) | ||||
| { | ||||
| 	return dmeventd_lvm2_run(cmd); | ||||
| } | ||||
|  | ||||
| static void _umount(const char *device, int major, int minor) | ||||
| { | ||||
| 	FILE *mounts; | ||||
| 	char buffer[4096]; | ||||
| 	char *words[3]; | ||||
| 	struct stat st; | ||||
|  | ||||
| 	if (!(mounts = fopen("/proc/mounts", "r"))) { | ||||
| 		syslog(LOG_ERR, "Could not read /proc/mounts. Not umounting %s.\n", 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) && | ||||
| 		    major(st.st_rdev) == major && | ||||
| 		    minor(st.st_rdev) == minor) { | ||||
| 			syslog(LOG_ERR, "Unmounting invalid snapshot %s from %s.\n", device, words[1]); | ||||
|                         if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL)) | ||||
|                                 syslog(LOG_ERR, "Failed to umount snapshot %s from %s: %s.\n", | ||||
|                                        device, words[1], strerror(errno)); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (fclose(mounts)) | ||||
| 		syslog(LOG_ERR, "Failed to close /proc/mounts.\n"); | ||||
| } | ||||
|  | ||||
| void process_event(struct dm_task *dmt, | ||||
| 		   enum dm_event_mask event __attribute__((unused)), | ||||
| 		   void **private) | ||||
| { | ||||
| 	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 dso_state *state = *private; | ||||
|  | ||||
| 	/* No longer monitoring, waiting for remove */ | ||||
| 	if (!state->percent_check) | ||||
| 		return; | ||||
|  | ||||
| 	dmeventd_lvm2_lock(); | ||||
|  | ||||
| 	dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); | ||||
| 	if (!target_type) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_get_status_snapshot(state->mem, params, &status)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (status->invalid) { | ||||
| 		struct dm_info info; | ||||
| 		if (dm_task_get_info(dmt, &info)) { | ||||
| 			dmeventd_lvm2_unlock(); | ||||
| 			_umount(device, info.major, info.minor); | ||||
| 			return; | ||||
| 		} /* else; too bad, but this is best-effort thing... */ | ||||
| 	} | ||||
|  | ||||
| 	/* 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; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * 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->total_sectors) { | ||||
| 		syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params); | ||||
| 		state->percent_check = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	percent = (int) (100 * 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. */ | ||||
| 			syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent); | ||||
| 		/* Try to extend the snapshot, in accord with user-set policies */ | ||||
| 		if (!_extend(state->cmd_str)) | ||||
| 			syslog(LOG_ERR, "Failed to extend snapshot %s.\n", device); | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	if (status) | ||||
| 		dm_pool_free(state->mem, status); | ||||
| 	dmeventd_lvm2_unlock(); | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| 		    const char *uuid __attribute__((unused)), | ||||
| 		    int major __attribute__((unused)), | ||||
| 		    int minor __attribute__((unused)), | ||||
| 		    void **private) | ||||
| { | ||||
| 	struct dm_pool *statemem = NULL; | ||||
| 	struct dso_state *state; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_init()) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!(statemem = dm_pool_create("snapshot_state", 512)) || | ||||
| 	    !(state = dm_pool_zalloc(statemem, sizeof(*state)))) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(statemem, state->cmd_str, | ||||
| 				   sizeof(state->cmd_str), | ||||
| 				   "lvextend --use-policies", device)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	state->mem = statemem; | ||||
| 	state->percent_check = CHECK_MINIMUM; | ||||
| 	*private = state; | ||||
|  | ||||
| 	syslog(LOG_INFO, "Monitoring snapshot %s\n", device); | ||||
|  | ||||
| 	return 1; | ||||
| bad: | ||||
| 	if (statemem) | ||||
| 		dm_pool_destroy(statemem); | ||||
| 	dmeventd_lvm2_exit(); | ||||
| out: | ||||
| 	syslog(LOG_ERR, "Failed to monitor snapshot %s.\n", device); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int unregister_device(const char *device, | ||||
| 		      const char *uuid __attribute__((unused)), | ||||
| 		      int major __attribute__((unused)), | ||||
| 		      int minor __attribute__((unused)), | ||||
| 		      void **private) | ||||
| { | ||||
| 	struct dso_state *state = *private; | ||||
|  | ||||
| 	syslog(LOG_INFO, "No longer monitoring snapshot %s\n", device); | ||||
| 	dm_pool_destroy(state->mem); | ||||
| 	dmeventd_lvm2_exit(); | ||||
|  | ||||
| 	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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,401 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2011-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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
|  | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd_lvm.h" | ||||
|  | ||||
| #include <sys/wait.h> | ||||
| #include <syslog.h> /* FIXME Replace syslog with multilog */ | ||||
| #include <stdarg.h> | ||||
| /* FIXME Missing openlog? */ | ||||
|  | ||||
| /* First warning when thin is 80% full. */ | ||||
| #define WARNING_THRESH 80 | ||||
| /* Run a check every 5%. */ | ||||
| #define CHECK_STEP 5 | ||||
| /* Do not bother checking thins less than 50% full. */ | ||||
| #define CHECK_MINIMUM 50 | ||||
|  | ||||
| #define UMOUNT_COMMAND "/bin/umount" | ||||
|  | ||||
| #define THIN_DEBUG 0 | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	int metadata_percent_check; | ||||
| 	int data_percent_check; | ||||
| 	uint64_t known_metadata_size; | ||||
| 	uint64_t known_data_size; | ||||
| 	char cmd_str[1024]; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* 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)) | ||||
| #  define MKDEV(x,y) makedev((x),(y)) | ||||
| #endif | ||||
|  | ||||
| /* Get dependencies for device, and try to find matching device */ | ||||
| static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_minor) | ||||
| { | ||||
| 	struct dm_task *dmt; | ||||
| 	const struct dm_deps *deps; | ||||
| 	struct dm_info info; | ||||
| 	int major, minor; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	if (!(dmt = dm_task_create(DM_DEVICE_DEPS))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!dm_task_set_name(dmt, name)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_task_no_open_count(dmt)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_task_run(dmt)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_task_get_info(dmt, &info)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!(deps = dm_task_get_deps(dmt))) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!info.exists || deps->count != 1) | ||||
| 		goto out; | ||||
|  | ||||
| 	major = (int) MAJOR(deps->device[0]); | ||||
| 	minor = (int) MINOR(deps->device[0]); | ||||
| 	if ((major != tp_major) || (minor != tp_minor)) | ||||
| 		goto out; | ||||
|  | ||||
| 	*dev_minor = info.minor; | ||||
|  | ||||
| #if THIN_DEBUG | ||||
| 	{ | ||||
| 		char dev_name[PATH_MAX]; | ||||
| 		if (dm_device_get_name(major, minor, 0, dev_name, sizeof(dev_name))) | ||||
| 			syslog(LOG_DEBUG, "Found %s (%u:%u) depends on %s", | ||||
| 			       name, major, *dev_minor, dev_name); | ||||
| 	} | ||||
| #endif | ||||
| 	r = 1; | ||||
| out: | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* Get all active devices */ | ||||
| static int _find_all_devs(dm_bitset_t bs, int tp_major, int tp_minor) | ||||
| { | ||||
| 	struct dm_task *dmt; | ||||
| 	struct dm_names *names; | ||||
| 	unsigned next = 0; | ||||
| 	int minor, r = 1; | ||||
|  | ||||
| 	if (!(dmt = dm_task_create(DM_DEVICE_LIST))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!dm_task_run(dmt)) { | ||||
| 		r = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!(names = dm_task_get_names(dmt))) { | ||||
| 		r = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!names->dev) | ||||
| 		goto out; | ||||
|  | ||||
| 	do { | ||||
| 		names = (struct dm_names *)((char *) names + next); | ||||
| 		if (_has_deps(names->name, tp_major, tp_minor, &minor)) | ||||
| 			dm_bit_set(bs, minor); | ||||
| 		next = names->next; | ||||
| 	} while (next); | ||||
|  | ||||
| out: | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _extend(struct dso_state *state) | ||||
| { | ||||
| #if THIN_DEBUG | ||||
| 	syslog(LOG_INFO, "dmeventd executes: %s.\n", state->cmd_str); | ||||
| #endif | ||||
| 	return dmeventd_lvm2_run(state->cmd_str); | ||||
| } | ||||
|  | ||||
| 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); | ||||
| 		syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno)); | ||||
| 		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 */ | ||||
| } | ||||
|  | ||||
| struct mountinfo_s { | ||||
| 	struct dm_info info; | ||||
| 	dm_bitset_t minors; /* Bitset for active thin pool minors */ | ||||
| 	const char *device; | ||||
| }; | ||||
|  | ||||
| static int _umount_device(char *buffer, unsigned major, unsigned minor, | ||||
| 			  char *target, void *cb_data) | ||||
| { | ||||
| 	struct mountinfo_s *data = cb_data; | ||||
|  | ||||
| 	if ((major == data->info.major) && dm_bit(data->minors, minor)) { | ||||
| 		syslog(LOG_INFO, "Unmounting thin volume %s from %s.\n", | ||||
| 		       data->device, target); | ||||
| 		if (!_run(UMOUNT_COMMAND, "-fl", target, NULL)) | ||||
| 			syslog(LOG_ERR, "Failed to umount thin %s from %s: %s.\n", | ||||
| 			       data->device, target, strerror(errno)); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Find all thin pool users and try to umount them. | ||||
|  * TODO: work with read-only thin pool support | ||||
|  */ | ||||
| static void _umount(struct dm_task *dmt, const char *device) | ||||
| { | ||||
| 	/* TODO: Convert to use hash to reduce memory usage */ | ||||
| 	static const size_t MINORS = (1U << 20); /* 20 bit */ | ||||
| 	struct mountinfo_s data = { | ||||
| 		.device = device, | ||||
| 	}; | ||||
|  | ||||
| 	if (!dm_task_get_info(dmt, &data.info)) | ||||
| 		return; | ||||
|  | ||||
| 	dmeventd_lvm2_unlock(); | ||||
|  | ||||
| 	if (!(data.minors = dm_bitset_create(NULL, MINORS))) { | ||||
| 		syslog(LOG_ERR, "Failed to allocate bitset. Not unmounting %s.\n", device); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!_find_all_devs(data.minors, data.info.major, data.info.minor)) { | ||||
| 		syslog(LOG_ERR, "Failed to detect mounted volumes for %s.\n", device); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!dm_mountinfo_read(_umount_device, &data)) { | ||||
| 		syslog(LOG_ERR, "Could not parse mountinfo file.\n"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	if (data.minors) | ||||
| 		dm_bitset_destroy(data.minors); | ||||
| 	dmeventd_lvm2_lock(); | ||||
| } | ||||
|  | ||||
| void process_event(struct dm_task *dmt, | ||||
| 		   enum dm_event_mask event __attribute__((unused)), | ||||
| 		   void **private) | ||||
| { | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
| 	int percent; | ||||
| 	struct dso_state *state = *private; | ||||
| 	struct dm_status_thin_pool *tps = NULL; | ||||
| 	void *next = NULL; | ||||
| 	uint64_t start, length; | ||||
| 	char *target_type = NULL; | ||||
| 	char *params; | ||||
|  | ||||
| #if 0 | ||||
| 	/* No longer monitoring, waiting for remove */ | ||||
| 	if (!state->meta_percent_check && !state->data_percent_check) | ||||
| 		return; | ||||
| #endif | ||||
| 	dmeventd_lvm2_lock(); | ||||
|  | ||||
| 	dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); | ||||
|  | ||||
| 	if (!target_type || (strcmp(target_type, "thin-pool") != 0)) { | ||||
| 		syslog(LOG_ERR, "Invalid target type.\n"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!dm_get_status_thin_pool(state->mem, params, &tps)) { | ||||
| 		syslog(LOG_ERR, "Failed to parse status.\n"); | ||||
| 		_umount(dmt, device); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| #if THIN_DEBUG | ||||
| 	syslog(LOG_INFO, "%p: Got status %" PRIu64 " / %" PRIu64 | ||||
| 	       " %" PRIu64  " / %" PRIu64 ".\n", state, | ||||
| 	       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; | ||||
| 	} | ||||
|  | ||||
| 	if (state->known_data_size != tps->total_data_blocks) { | ||||
| 		state->data_percent_check = CHECK_MINIMUM; | ||||
| 		state->known_data_size = tps->total_data_blocks; | ||||
| 	} | ||||
|  | ||||
| 	percent = 100 * tps->used_metadata_blocks / tps->total_metadata_blocks; | ||||
| 	if (percent >= state->metadata_percent_check) { | ||||
| 		/* | ||||
| 		 * Usage has raised more than CHECK_STEP since the last | ||||
| 		 * time. Run actions. | ||||
| 		 */ | ||||
| 		state->metadata_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP; | ||||
|  | ||||
| 		/* FIXME: extension of metadata needs to be written! */ | ||||
| 		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ | ||||
| 			syslog(LOG_WARNING, "Thin metadata %s is now %i%% full.\n", | ||||
| 			       device, percent); | ||||
| 		 /* Try to extend the metadata, in accord with user-set policies */ | ||||
| 		if (!_extend(state)) { | ||||
| 			syslog(LOG_ERR, "Failed to extend thin metadata %s.\n", | ||||
| 			       device); | ||||
| 			_umount(dmt, device); | ||||
| 		} | ||||
| 		/* FIXME: hmm READ-ONLY switch should happen in error path */ | ||||
| 	} | ||||
|  | ||||
| 	percent = 100 * tps->used_data_blocks / tps->total_data_blocks; | ||||
| 	if (percent >= state->data_percent_check) { | ||||
| 		/* | ||||
| 		 * Usage has raised more than CHECK_STEP since | ||||
| 		 * the last time. Run actions. | ||||
| 		 */ | ||||
| 		state->data_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP; | ||||
|  | ||||
| 		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ | ||||
| 			syslog(LOG_WARNING, "Thin %s is now %i%% full.\n", device, percent); | ||||
| 		/* Try to extend the thin data, in accord with user-set policies */ | ||||
| 		if (!_extend(state)) { | ||||
| 			syslog(LOG_ERR, "Failed to extend thin %s.\n", device); | ||||
| 			state->data_percent_check = 0; | ||||
| 			_umount(dmt, device); | ||||
| 		} | ||||
| 		/* FIXME: hmm READ-ONLY switch should happen in error path */ | ||||
| 	} | ||||
| out: | ||||
| 	if (tps) | ||||
| 		dm_pool_free(state->mem, tps); | ||||
|  | ||||
| 	dmeventd_lvm2_unlock(); | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| 		    const char *uuid __attribute__((unused)), | ||||
| 		    int major __attribute__((unused)), | ||||
| 		    int minor __attribute__((unused)), | ||||
| 		    void **private) | ||||
| { | ||||
| 	struct dm_pool *statemem = NULL; | ||||
| 	struct dso_state *state; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_init()) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (!(statemem = dm_pool_create("thin_pool_state", 2048)) || | ||||
| 	    !(state = dm_pool_zalloc(statemem, sizeof(*state))) || | ||||
| 	    !dmeventd_lvm2_command(statemem, state->cmd_str, | ||||
| 				   sizeof(state->cmd_str), | ||||
| 				   "lvextend --use-policies", | ||||
| 				   device)) { | ||||
| 		if (statemem) | ||||
| 			dm_pool_destroy(statemem); | ||||
| 		dmeventd_lvm2_exit(); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	state->mem = statemem; | ||||
| 	state->metadata_percent_check = CHECK_MINIMUM; | ||||
| 	state->data_percent_check = CHECK_MINIMUM; | ||||
| 	*private = state; | ||||
|  | ||||
| 	syslog(LOG_INFO, "Monitoring thin %s.\n", device); | ||||
|  | ||||
| 	return 1; | ||||
| bad: | ||||
| 	syslog(LOG_ERR, "Failed to monitor thin %s.\n", device); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int unregister_device(const char *device, | ||||
| 		      const char *uuid __attribute__((unused)), | ||||
| 		      int major __attribute__((unused)), | ||||
| 		      int minor __attribute__((unused)), | ||||
| 		      void **private) | ||||
| { | ||||
| 	struct dso_state *state = *private; | ||||
|  | ||||
| 	syslog(LOG_INFO, "No longer monitoring thin %s.\n", device); | ||||
| 	dm_pool_destroy(state->mem); | ||||
| 	dmeventd_lvm2_exit(); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										2
									
								
								daemons/lvmetad/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								daemons/lvmetad/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +0,0 @@ | ||||
| lvmetad | ||||
| lvmetactl | ||||
| @@ -1,63 +0,0 @@ | ||||
| # | ||||
| # Copyright (C) 2011-2012 Red Hat, Inc. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| # This copyrighted material is made available to anyone wishing to use, | ||||
| # modify, copy, or redistribute it subject to the terms and conditions | ||||
| # of the GNU Lesser General Public License v.2.1. | ||||
| # | ||||
| # You should have received a copy of the GNU Lesser General Public License | ||||
| # along with this program; if not, write to the Free Software Foundation, | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| SOURCES = lvmetad-core.c | ||||
| SOURCES2 = testclient.c | ||||
|  | ||||
| TARGETS = lvmetad lvmetactl | ||||
|  | ||||
| .PHONY: install_lvmetad | ||||
|  | ||||
| CFLOW_LIST = $(SOURCES) | ||||
| CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
| CFLOW_TARGET = lvmetad | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| INCLUDES += -I$(top_srcdir)/libdaemon/server | ||||
| LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper | ||||
|  | ||||
| LIBS += $(PTHREAD_LIBS) | ||||
|  | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) | ||||
| CLDFLAGS += -L$(top_builddir)/libdaemon/server | ||||
| CFLAGS += $(EXTRA_EXEC_CFLAGS) | ||||
|  | ||||
| lvmetad: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 		    $(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS) | ||||
|  | ||||
| lvmetactl: lvmetactl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 	$(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmetactl.o $(LVMLIBS) | ||||
|  | ||||
| # TODO: No idea. No idea how to test either. | ||||
| #ifneq ("$(CFLOW_CMD)", "") | ||||
| #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_lvmetad: lvmetad | ||||
| 	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F) | ||||
|  | ||||
| install_lvm2: install_lvmetad | ||||
|  | ||||
| install: install_lvm2 | ||||
| @@ -1,183 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014 Red Hat, Inc. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU Lesser General Public License v.2.1. | ||||
|  */ | ||||
|  | ||||
| #include "tool.h" | ||||
|  | ||||
| #include "lvmetad-client.h" | ||||
|  | ||||
| daemon_handle h; | ||||
|  | ||||
| static void print_reply(daemon_reply reply) | ||||
| { | ||||
| 	const char *a = daemon_reply_str(reply, "response", NULL); | ||||
| 	const char *b = daemon_reply_str(reply, "status", NULL); | ||||
| 	const char *c = daemon_reply_str(reply, "reason", NULL); | ||||
|  | ||||
| 	printf("response \"%s\" status \"%s\" reason \"%s\"\n", | ||||
| 	       a ? a : "", b ? b : "", c ? c : ""); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	daemon_reply reply; | ||||
| 	char *cmd; | ||||
| 	char *uuid; | ||||
| 	char *name; | ||||
| 	int val; | ||||
| 	int ver; | ||||
|  | ||||
| 	if (argc < 2) { | ||||
| 		printf("lvmeta dump\n"); | ||||
| 		printf("lvmeta pv_list\n"); | ||||
| 		printf("lvmeta vg_list\n"); | ||||
| 		printf("lvmeta vg_lookup_name <name>\n"); | ||||
| 		printf("lvmeta vg_lookup_uuid <uuid>\n"); | ||||
| 		printf("lvmeta pv_lookup_uuid <uuid>\n"); | ||||
| 		printf("lvmeta set_global_invalid 0|1\n"); | ||||
| 		printf("lvmeta get_global_invalid\n"); | ||||
| 		printf("lvmeta set_vg_version <uuid> <version>\n"); | ||||
| 		printf("lvmeta vg_lock_type <uuid>\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	cmd = argv[1]; | ||||
|  | ||||
| 	h = lvmetad_open(NULL); | ||||
|  | ||||
| 	if (!strcmp(cmd, "dump")) { | ||||
| 		reply = daemon_send_simple(h, "dump", | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "pv_list")) { | ||||
| 		reply = daemon_send_simple(h, "pv_list", | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "vg_list")) { | ||||
| 		reply = daemon_send_simple(h, "vg_list", | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "set_global_invalid")) { | ||||
| 		if (argc < 3) { | ||||
| 			printf("set_global_invalid 0|1\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		val = atoi(argv[2]); | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "set_global_info", | ||||
| 					   "global_invalid = %d", val, | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		print_reply(reply); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "get_global_invalid")) { | ||||
| 		reply = daemon_send_simple(h, "get_global_info", | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "set_vg_version")) { | ||||
| 		if (argc < 4) { | ||||
| 			printf("set_vg_version <uuid> <ver>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		uuid = argv[2]; | ||||
| 		ver = atoi(argv[3]); | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "set_vg_info", | ||||
| 					   "uuid = %s", uuid, | ||||
| 					   "version = %d", ver, | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		print_reply(reply); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "vg_lookup_name")) { | ||||
| 		if (argc < 3) { | ||||
| 			printf("vg_lookup_name <name>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		name = argv[2]; | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "vg_lookup", | ||||
| 					   "name = %s", name, | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "vg_lookup_uuid")) { | ||||
| 		if (argc < 3) { | ||||
| 			printf("vg_lookup_uuid <uuid>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		uuid = argv[2]; | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "vg_lookup", | ||||
| 					   "uuid = %s", uuid, | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "vg_lock_type")) { | ||||
| 		struct dm_config_node *metadata; | ||||
| 		const char *lock_type; | ||||
|  | ||||
| 		if (argc < 3) { | ||||
| 			printf("vg_lock_type <uuid>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		uuid = argv[2]; | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "vg_lookup", | ||||
| 					   "uuid = %s", uuid, | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		/* printf("%s\n", reply.buffer.mem); */ | ||||
|  | ||||
| 		metadata = dm_config_find_node(reply.cft->root, "metadata"); | ||||
| 		if (!metadata) { | ||||
| 			printf("no metadata\n"); | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		lock_type = dm_config_find_str(metadata, "metadata/lock_type", NULL); | ||||
| 		if (!lock_type) { | ||||
| 			printf("no lock_type\n"); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		printf("lock_type %s\n", lock_type); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "pv_lookup_uuid")) { | ||||
| 		if (argc < 3) { | ||||
| 			printf("pv_lookup_uuid <uuid>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		uuid = argv[2]; | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "pv_lookup", | ||||
| 					   "uuid = %s", uuid, | ||||
| 					   "token = %s", "skip", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else { | ||||
| 		printf("unknown command\n"); | ||||
| 		goto out_close; | ||||
| 	} | ||||
| out: | ||||
| 	daemon_reply_destroy(reply); | ||||
| out_close: | ||||
| 	daemon_close(h); | ||||
| 	return 0; | ||||
| } | ||||
| @@ -1,83 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2011-2012 Red Hat, Inc. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU Lesser General Public License v.2.1. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with this program; if not, write to the Free Software Foundation, | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_LVMETAD_CLIENT_H | ||||
| #define _LVM_LVMETAD_CLIENT_H | ||||
|  | ||||
| #include "daemon-client.h" | ||||
|  | ||||
| #define LVMETAD_SOCKET DEFAULT_RUN_DIR "/lvmetad.socket" | ||||
|  | ||||
| struct volume_group; | ||||
|  | ||||
| /* Different types of replies we may get from lvmetad. */ | ||||
|  | ||||
| typedef struct { | ||||
| 	daemon_reply r; | ||||
| 	const char **uuids; /* NULL terminated array */ | ||||
| } lvmetad_uuidlist; | ||||
|  | ||||
| typedef struct { | ||||
| 	daemon_reply r; | ||||
| 	struct dm_config_tree *cft; | ||||
| } lvmetad_vg; | ||||
|  | ||||
| /* Get a list of VG UUIDs that match a given VG name. */ | ||||
| lvmetad_uuidlist lvmetad_lookup_vgname(daemon_handle h, const char *name); | ||||
|  | ||||
| /* Get the metadata of a single VG, identified by UUID. */ | ||||
| lvmetad_vg lvmetad_get_vg(daemon_handle h, const char *uuid); | ||||
|  | ||||
| /* | ||||
|  * Add and remove PVs on demand. Udev-driven systems will use this interface | ||||
|  * instead of scanning. | ||||
|  */ | ||||
| daemon_reply lvmetad_add_pv(daemon_handle h, const char *pv_uuid, const char *mda_content); | ||||
| daemon_reply lvmetad_remove_pv(daemon_handle h, const char *pv_uuid); | ||||
|  | ||||
| /* Trigger a full disk scan, throwing away all caches. XXX do we eventually want | ||||
|  * this? Probably not yet, anyway. | ||||
|  *     daemon_reply lvmetad_rescan(daemon_handle h); | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Update the version of metadata of a volume group. The VG has to be locked for | ||||
|  * writing for this, and the VG metadata here has to match whatever has been | ||||
|  * written to the disk (under this lock). This initially avoids the requirement | ||||
|  * for lvmetad to write to disk (in later revisions, lvmetad_supersede_vg may | ||||
|  * also do the writing, or we probably add another function to do that). | ||||
|  */ | ||||
| daemon_reply lvmetad_supersede_vg(daemon_handle h, struct volume_group *vg); | ||||
|  | ||||
| /* Wrappers to open/close connection */ | ||||
|  | ||||
| static inline daemon_handle lvmetad_open(const char *socket) | ||||
| { | ||||
| 	daemon_info lvmetad_info = { | ||||
| 		.path = "lvmetad", | ||||
| 		.socket = socket ?: LVMETAD_SOCKET, | ||||
| 		.protocol = "lvmetad", | ||||
| 		.protocol_version = 1, | ||||
| 		.autostart = 0 | ||||
| 	}; | ||||
|  | ||||
| 	return daemon_open(lvmetad_info); | ||||
| } | ||||
|  | ||||
| static inline void lvmetad_close(daemon_handle h) | ||||
| { | ||||
| 	return daemon_close(h); | ||||
| } | ||||
|  | ||||
| #endif | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,16 +0,0 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| export LD_LIBRARY_PATH="$1" | ||||
|  | ||||
| test -n "$2" && { | ||||
|     rm -f /var/run/lvmetad.{socket,pid} | ||||
|     chmod +rx lvmetad | ||||
|     valgrind ./lvmetad -f & | ||||
|     PID=$! | ||||
|     sleep 1 | ||||
|     ./testclient | ||||
|     kill $PID | ||||
|     exit 0 | ||||
| } | ||||
|  | ||||
| sudo ./test.sh "$1" . | ||||
| @@ -1,144 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2011-2014 Red Hat, Inc. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU General Public License v.2. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software Foundation, | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #include "tool.h" | ||||
|  | ||||
| #include "lvmetad-client.h" | ||||
| #include "label.h" | ||||
| #include "lvmcache.h" | ||||
| #include "metadata.h" | ||||
|  | ||||
| const char *uuid1 = "abcd-efgh"; | ||||
| const char *uuid2 = "bbcd-efgh"; | ||||
| const char *vgid = "yada-yada"; | ||||
| const char *uuid3 = "cbcd-efgh"; | ||||
|  | ||||
| const char *metadata2 = "{\n" | ||||
| 	"id = \"yada-yada\"\n" | ||||
| 	"seqno = 15\n" | ||||
| 	"status = [\"READ\", \"WRITE\"]\n" | ||||
| 	"flags = []\n" | ||||
| 	"extent_size = 8192\n" | ||||
| 	"physical_volumes {\n" | ||||
| 	"    pv0 {\n" | ||||
| 	"        id = \"abcd-efgh\"\n" | ||||
| 	"    }\n" | ||||
| 	"    pv1 {\n" | ||||
| 	"        id = \"bbcd-efgh\"\n" | ||||
| 	"    }\n" | ||||
| 	"    pv2 {\n" | ||||
| 	"        id = \"cbcd-efgh\"\n" | ||||
| 	"    }\n" | ||||
| 	"}\n" | ||||
| 	"}\n"; | ||||
|  | ||||
| void _handle_reply(daemon_reply reply) { | ||||
| 	const char *repl = daemon_reply_str(reply, "response", NULL); | ||||
| 	const char *status = daemon_reply_str(reply, "status", NULL); | ||||
| 	const char *vgid = daemon_reply_str(reply, "vgid", NULL); | ||||
|  | ||||
| 	fprintf(stderr, "[C] REPLY: %s\n", repl); | ||||
| 	if (!strcmp(repl, "failed")) | ||||
| 		fprintf(stderr, "[C] REASON: %s\n", daemon_reply_str(reply, "reason", "unknown")); | ||||
| 	if (vgid) | ||||
| 		fprintf(stderr, "[C] VGID: %s\n", vgid); | ||||
| 	if (status) | ||||
| 		fprintf(stderr, "[C] STATUS: %s\n", status); | ||||
| 	daemon_reply_destroy(reply); | ||||
| } | ||||
|  | ||||
| void _pv_add(daemon_handle h, const char *uuid, const char *metadata) | ||||
| { | ||||
| 	daemon_reply reply = daemon_send_simple(h, "pv_add", "uuid = %s", uuid, | ||||
| 						             "metadata = %b", metadata, | ||||
| 						             NULL); | ||||
| 	_handle_reply(reply); | ||||
| } | ||||
|  | ||||
| int scan(daemon_handle h, char *fn) { | ||||
| 	struct device *dev = dev_cache_get(fn, NULL); | ||||
|  | ||||
| 	struct label *label; | ||||
| 	if (!label_read(dev, &label, 0)) { | ||||
| 		fprintf(stderr, "[C] no label found on %s\n", fn); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	char uuid[64]; | ||||
| 	id_write_format(dev->pvid, uuid, 64); | ||||
| 	fprintf(stderr, "[C] found PV: %s\n", uuid); | ||||
| 	struct lvmcache_info *info = (struct lvmcache_info *) label->info; | ||||
| 	struct physical_volume pv = { 0, }; | ||||
|  | ||||
| 	if (!(info->fmt->ops->pv_read(info->fmt, dev_name(dev), &pv, 0))) { | ||||
| 		fprintf(stderr, "[C] Failed to read PV %s", dev_name(dev)); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	struct format_instance_ctx fic; | ||||
| 	struct format_instance *fid = info->fmt->ops->create_instance(info->fmt, &fic); | ||||
| 	struct metadata_area *mda; | ||||
| 	struct volume_group *vg = NULL; | ||||
| 	dm_list_iterate_items(mda, &info->mdas) { | ||||
| 		struct volume_group *this = mda->ops->vg_read(fid, "", mda); | ||||
| 		if (this && !vg || this->seqno > vg->seqno) | ||||
| 			vg = this; | ||||
| 	} | ||||
| 	if (vg) { | ||||
| 		char *buf = NULL; | ||||
| 		/* TODO. This is not entirely correct, since export_vg_to_buffer | ||||
| 		 * adds trailing garbage to the buffer. We may need to use | ||||
| 		 * export_vg_to_config_tree and format the buffer ourselves. It | ||||
| 		 * does, however, work for now, since the garbage is well | ||||
| 		 * formatted and has no conflicting keys with the rest of the | ||||
| 		 * request.  */ | ||||
| 		export_vg_to_buffer(vg, &buf); | ||||
| 		daemon_reply reply = | ||||
| 			daemon_send_simple(h, "pv_add", "uuid = %s", uuid, | ||||
| 					      "metadata = %b", strchr(buf, '{'), | ||||
| 					      NULL); | ||||
| 		_handle_reply(reply); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void _dump_vg(daemon_handle h, const char *uuid) | ||||
| { | ||||
| 	daemon_reply reply = daemon_send_simple(h, "vg_by_uuid", "uuid = %s", uuid, NULL); | ||||
| 	fprintf(stderr, "[C] reply buffer: %s\n", reply.buffer); | ||||
| 	daemon_reply_destroy(reply); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) { | ||||
| 	daemon_handle h = lvmetad_open(); | ||||
| 	/* FIXME Missing error path */ | ||||
|  | ||||
| 	if (argc > 1) { | ||||
| 		int i; | ||||
| 		struct cmd_context *cmd = create_toolcontext(0, NULL, 0, 0, 1, 1); | ||||
| 		for (i = 1; i < argc; ++i) { | ||||
| 			const char *uuid = NULL; | ||||
| 			scan(h, argv[i]); | ||||
| 		} | ||||
| 		destroy_toolcontext(cmd); | ||||
| 		/* FIXME Missing lvmetad_close() */ | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	_pv_add(h, uuid1, NULL); | ||||
| 	_pv_add(h, uuid2, metadata2); | ||||
| 	_dump_vg(h, vgid); | ||||
| 	_pv_add(h, uuid3, NULL); | ||||
|  | ||||
| 	daemon_close(h);	/* FIXME lvmetad_close? */ | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										2
									
								
								daemons/lvmlockd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								daemons/lvmlockd/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +0,0 @@ | ||||
| lvmlockctl | ||||
| lvmlockd | ||||
| @@ -1,66 +0,0 @@ | ||||
| # | ||||
| # Copyright (C) 2014-2015 Red Hat, Inc. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| # This copyrighted material is made available to anyone wishing to use, | ||||
| # modify, copy, or redistribute it subject to the terms and conditions | ||||
| # of the GNU Lesser General Public License v.2.1. | ||||
| # | ||||
| # You should have received a copy of the GNU Lesser General Public License | ||||
| # along with this program; if not, write to the Free Software Foundation, | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| SOURCES = lvmlockd-core.c | ||||
|  | ||||
| ifeq ("@BUILD_LOCKDSANLOCK@", "yes") | ||||
|   SOURCES += lvmlockd-sanlock.c | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_LOCKDDLM@", "yes") | ||||
|   SOURCES += lvmlockd-dlm.c | ||||
| endif | ||||
|  | ||||
| TARGETS = lvmlockd lvmlockctl | ||||
|  | ||||
| .PHONY: install_lvmlockd | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| INCLUDES += -I$(top_srcdir)/libdaemon/server | ||||
| LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper | ||||
|  | ||||
| LIBS += $(PTHREAD_LIBS) | ||||
|  | ||||
| ifeq ("@BUILD_LOCKDSANLOCK@", "yes") | ||||
|   LIBS += -lsanlock_client | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_LOCKDDLM@", "yes") | ||||
|   LIBS += -ldlm_lt | ||||
| endif | ||||
|  | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server | ||||
| CLDFLAGS += -L$(top_builddir)/libdaemon/server | ||||
|  | ||||
| lvmlockd: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 		    $(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS) | ||||
|  | ||||
| lvmlockctl: lvmlockctl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 		    $(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmlockctl.o $(LVMLIBS) | ||||
|  | ||||
| install_lvmlockd: lvmlockd | ||||
| 	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F) | ||||
|  | ||||
| install_lvmlockctl: lvmlockctl | ||||
| 	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F) | ||||
|  | ||||
| install_lvm2: install_lvmlockd install_lvmlockctl | ||||
|  | ||||
| install: install_lvm2 | ||||
| @@ -1,745 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014-2015 Red Hat, Inc. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU Lesser General Public License v.2.1. | ||||
|  */ | ||||
|  | ||||
| #include "tool.h" | ||||
|  | ||||
| #include "lvmlockd-client.h" | ||||
|  | ||||
| #include <stddef.h> | ||||
| #include <getopt.h> | ||||
| #include <signal.h> | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <syslog.h> | ||||
| #include <sys/wait.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/un.h> | ||||
|  | ||||
| static int quit = 0; | ||||
| static int info = 0; | ||||
| static int dump = 0; | ||||
| static int wait_opt = 0; | ||||
| static int force_opt = 0; | ||||
| static int kill_vg = 0; | ||||
| static int drop_vg = 0; | ||||
| static int gl_enable = 0; | ||||
| static int gl_disable = 0; | ||||
| static int stop_lockspaces = 0; | ||||
| static char *arg_vg_name = NULL; | ||||
|  | ||||
| #define DUMP_SOCKET_NAME "lvmlockd-dump.sock" | ||||
| #define DUMP_BUF_SIZE (1024 * 1024) | ||||
| static char dump_buf[DUMP_BUF_SIZE+1]; | ||||
| static int dump_len; | ||||
| static struct sockaddr_un dump_addr; | ||||
| static socklen_t dump_addrlen; | ||||
|  | ||||
| daemon_handle _lvmlockd; | ||||
|  | ||||
| #define log_error(fmt, args...) \ | ||||
| do { \ | ||||
| 	printf(fmt "\n", ##args); \ | ||||
| } while (0) | ||||
|  | ||||
| #define MAX_LINE 512 | ||||
|  | ||||
| /* copied from lvmlockd-internal.h */ | ||||
| #define MAX_NAME 64 | ||||
| #define MAX_ARGS 64 | ||||
|  | ||||
| /* | ||||
|  * lvmlockd dumps the client info before the lockspaces, | ||||
|  * so we can look up client info when printing lockspace info. | ||||
|  */ | ||||
|  | ||||
| #define MAX_CLIENTS 100 | ||||
|  | ||||
| struct client_info { | ||||
| 	uint32_t client_id; | ||||
| 	int pid; | ||||
| 	char name[MAX_NAME+1]; | ||||
| }; | ||||
|  | ||||
| static struct client_info clients[MAX_CLIENTS]; | ||||
| static int num_clients; | ||||
|  | ||||
| static void save_client_info(char *line) | ||||
| { | ||||
| 	uint32_t pid = 0; | ||||
| 	int fd = 0; | ||||
| 	int pi = 0; | ||||
| 	uint32_t client_id = 0; | ||||
| 	char name[MAX_NAME+1] = { 0 }; | ||||
|  | ||||
| 	sscanf(line, "info=client pid=%u fd=%d pi=%d id=%u name=%s", | ||||
| 	       &pid, &fd, &pi, &client_id, name); | ||||
|  | ||||
| 	clients[num_clients].client_id = client_id; | ||||
| 	clients[num_clients].pid = pid; | ||||
| 	strcpy(clients[num_clients].name, name); | ||||
| 	num_clients++; | ||||
| } | ||||
|  | ||||
| static void find_client_info(uint32_t client_id, uint32_t *pid, char *cl_name) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < num_clients; i++) { | ||||
| 		if (clients[i].client_id == client_id) { | ||||
| 			*pid = clients[i].pid; | ||||
| 			strcpy(cl_name, clients[i].name); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int first_ls = 1; | ||||
|  | ||||
| static void format_info_ls(char *line) | ||||
| { | ||||
| 	char ls_name[MAX_NAME+1] = { 0 }; | ||||
| 	char vg_name[MAX_NAME+1] = { 0 }; | ||||
| 	char vg_uuid[MAX_NAME+1] = { 0 }; | ||||
| 	char vg_sysid[MAX_NAME+1] = { 0 }; | ||||
| 	char lock_args[MAX_ARGS+1] = { 0 }; | ||||
| 	char lock_type[MAX_NAME+1] = { 0 }; | ||||
|  | ||||
| 	sscanf(line, "info=ls ls_name=%s vg_name=%s vg_uuid=%s vg_sysid=%s vg_args=%s lm_type=%s", | ||||
| 	       ls_name, vg_name, vg_uuid, vg_sysid, lock_args, lock_type); | ||||
|  | ||||
| 	if (!first_ls) | ||||
| 		printf("\n"); | ||||
| 	first_ls = 0; | ||||
|  | ||||
| 	printf("VG %s lock_type=%s %s\n", vg_name, lock_type, vg_uuid); | ||||
|  | ||||
| 	printf("LS %s %s\n", lock_type, ls_name); | ||||
| } | ||||
|  | ||||
| static void format_info_ls_action(char *line) | ||||
| { | ||||
| 	uint32_t client_id = 0; | ||||
| 	char flags[MAX_NAME+1] = { 0 }; | ||||
| 	char version[MAX_NAME+1] = { 0 }; | ||||
| 	char op[MAX_NAME+1] = { 0 }; | ||||
| 	uint32_t pid = 0; | ||||
| 	char cl_name[MAX_NAME+1] = { 0 }; | ||||
|  | ||||
| 	sscanf(line, "info=ls_action client_id=%u %s %s op=%s", | ||||
| 	       &client_id, flags, version, op); | ||||
|  | ||||
| 	find_client_info(client_id, &pid, cl_name); | ||||
|  | ||||
| 	printf("OP %s pid %u (%s)\n", op, pid, cl_name); | ||||
| } | ||||
|  | ||||
| static void format_info_r(char *line, char *r_name_out, char *r_type_out) | ||||
| { | ||||
| 	char r_name[MAX_NAME+1] = { 0 }; | ||||
| 	char r_type[4] = { 0 }; | ||||
| 	char mode[4] = { 0 }; | ||||
| 	char sh_count[MAX_NAME+1] = { 0 }; | ||||
| 	uint32_t ver = 0; | ||||
|  | ||||
| 	sscanf(line, "info=r name=%s type=%s mode=%s %s version=%u", | ||||
| 	       r_name, r_type, mode, sh_count, &ver); | ||||
|  | ||||
| 	/* when mode is not un, wait and print each lk line */ | ||||
|  | ||||
| 	if (strcmp(mode, "un")) { | ||||
| 		strcpy(r_name_out, r_name); | ||||
| 		strcpy(r_type_out, r_type); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* when mode is un, there will be no lk lines, so print now */ | ||||
|  | ||||
| 	if (!strcmp(r_type, "gl")) { | ||||
| 		printf("LK GL un ver %u\n", ver); | ||||
|  | ||||
| 	} else if (!strcmp(r_type, "vg")) { | ||||
| 		printf("LK VG un ver %u\n", ver); | ||||
|  | ||||
| 	} else if (!strcmp(r_type, "lv")) { | ||||
| 		printf("LK LV un %s\n", r_name); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void format_info_lk(char *line, char *r_name, char *r_type) | ||||
| { | ||||
| 	char mode[4] = { 0 }; | ||||
| 	uint32_t ver = 0; | ||||
| 	char flags[MAX_NAME+1] = { 0 }; | ||||
| 	uint32_t client_id = 0; | ||||
| 	uint32_t pid = 0; | ||||
| 	char cl_name[MAX_NAME+1] = { 0 }; | ||||
|  | ||||
| 	if (!r_name[0] || !r_type[0]) { | ||||
| 		printf("format_info_lk error r_name %s r_type %s\n", r_name, r_type); | ||||
| 		printf("%s\n", line); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	sscanf(line, "info=lk mode=%s version=%u %s client_id=%u", | ||||
| 	       mode, &ver, flags, &client_id); | ||||
|  | ||||
| 	find_client_info(client_id, &pid, cl_name); | ||||
|  | ||||
| 	if (!strcmp(r_type, "gl")) { | ||||
| 		printf("LK GL %s ver %u pid %u (%s)\n", mode, ver, pid, cl_name); | ||||
|  | ||||
| 	} else if (!strcmp(r_type, "vg")) { | ||||
| 		printf("LK VG %s ver %u pid %u (%s)\n", mode, ver, pid, cl_name); | ||||
|  | ||||
| 	} else if (!strcmp(r_type, "lv")) { | ||||
| 		printf("LK LV %s %s\n", mode, r_name); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void format_info_r_action(char *line, char *r_name, char *r_type) | ||||
| { | ||||
| 	uint32_t client_id = 0; | ||||
| 	char flags[MAX_NAME+1] = { 0 }; | ||||
| 	char version[MAX_NAME+1] = { 0 }; | ||||
| 	char op[MAX_NAME+1] = { 0 }; | ||||
| 	char rt[4] = { 0 }; | ||||
| 	char mode[4] = { 0 }; | ||||
| 	char lm[MAX_NAME+1] = { 0 }; | ||||
| 	char result[MAX_NAME+1] = { 0 }; | ||||
| 	char lm_rv[MAX_NAME+1] = { 0 }; | ||||
| 	uint32_t pid = 0; | ||||
| 	char cl_name[MAX_NAME+1] = { 0 }; | ||||
|  | ||||
| 	if (!r_name[0] || !r_type[0]) { | ||||
| 		printf("format_info_r_action error r_name %s r_type %s\n", r_name, r_type); | ||||
| 		printf("%s\n", line); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	sscanf(line, "info=r_action client_id=%u %s %s op=%s rt=%s mode=%s %s %s %s", | ||||
| 	       &client_id, flags, version, op, rt, mode, lm, result, lm_rv); | ||||
|  | ||||
| 	find_client_info(client_id, &pid, cl_name); | ||||
|  | ||||
| 	if (strcmp(op, "lock")) { | ||||
| 		printf("OP %s pid %u (%s)", op, pid, cl_name); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (!strcmp(r_type, "gl")) { | ||||
| 		printf("LW GL %s ver %u pid %u (%s)\n", mode, 0, pid, cl_name); | ||||
|  | ||||
| 	} else if (!strcmp(r_type, "vg")) { | ||||
| 		printf("LW VG %s ver %u pid %u (%s)\n", mode, 0, pid, cl_name); | ||||
|  | ||||
| 	} else if (!strcmp(r_type, "lv")) { | ||||
| 		printf("LW LV %s %s\n", mode, r_name); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void format_info_line(char *line, char *r_name, char *r_type) | ||||
| { | ||||
| 	if (!strncmp(line, "info=structs ", strlen("info=structs "))) { | ||||
| 		/* only print this in the raw info dump */ | ||||
|  | ||||
| 	} else if (!strncmp(line, "info=client ", strlen("info=client "))) { | ||||
| 		save_client_info(line); | ||||
|  | ||||
| 	} else if (!strncmp(line, "info=ls ", strlen("info=ls "))) { | ||||
| 		format_info_ls(line); | ||||
|  | ||||
| 	} else if (!strncmp(line, "info=ls_action ", strlen("info=ls_action "))) { | ||||
| 		format_info_ls_action(line); | ||||
|  | ||||
| 	} else if (!strncmp(line, "info=r ", strlen("info=r "))) { | ||||
| 		/* | ||||
| 		 * r_name/r_type are reset when a new resource is found. | ||||
| 		 * They are reused for the lock and action lines that | ||||
| 		 * follow a resource line. | ||||
| 		 */ | ||||
| 		memset(r_name, 0, MAX_NAME+1); | ||||
| 		memset(r_type, 0, MAX_NAME+1); | ||||
| 		format_info_r(line, r_name, r_type); | ||||
|  | ||||
| 	} else if (!strncmp(line, "info=lk ", strlen("info=lk "))) { | ||||
| 		/* will use info from previous r */ | ||||
| 		format_info_lk(line, r_name, r_type); | ||||
|  | ||||
| 	} else if (!strncmp(line, "info=r_action ", strlen("info=r_action "))) { | ||||
| 		/* will use info from previous r */ | ||||
| 		format_info_r_action(line, r_name, r_type); | ||||
| 	} else { | ||||
| 		printf("UN %s\n", line); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void format_info(void) | ||||
| { | ||||
| 	char line[MAX_LINE]; | ||||
| 	char r_name[MAX_NAME+1]; | ||||
| 	char r_type[MAX_NAME+1]; | ||||
| 	int i, j; | ||||
|  | ||||
| 	j = 0; | ||||
| 	memset(line, 0, sizeof(line)); | ||||
|  | ||||
| 	for (i = 0; i < dump_len; i++) { | ||||
| 		line[j++] = dump_buf[i]; | ||||
|  | ||||
| 		if ((line[j-1] == '\n') || (line[j-1] == '\0')) { | ||||
| 			format_info_line(line, r_name, r_type); | ||||
| 			j = 0; | ||||
| 			memset(line, 0, sizeof(line)); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| static daemon_reply _lvmlockd_send(const char *req_name, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	daemon_reply repl; | ||||
| 	daemon_request req; | ||||
|  | ||||
| 	req = daemon_request_make(req_name); | ||||
|  | ||||
| 	va_start(ap, req_name); | ||||
| 	daemon_request_extend_v(req, ap); | ||||
| 	va_end(ap); | ||||
|  | ||||
| 	repl = daemon_send(_lvmlockd, req); | ||||
|  | ||||
| 	daemon_request_destroy(req); | ||||
|  | ||||
| 	return repl; | ||||
| } | ||||
|  | ||||
| /* See the same in lib/locking/lvmlockd.c */ | ||||
| #define NO_LOCKD_RESULT -1000 | ||||
|  | ||||
| static int _lvmlockd_result(daemon_reply reply, int *result) | ||||
| { | ||||
| 	int reply_result; | ||||
|  | ||||
| 	if (reply.error) { | ||||
| 		log_error("lvmlockd_result reply error %d", reply.error); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) { | ||||
| 		log_error("lvmlockd_result bad response"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	reply_result = daemon_reply_int(reply, "op_result", NO_LOCKD_RESULT); | ||||
| 	if (reply_result == -1000) { | ||||
| 		log_error("lvmlockd_result no op_result"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	*result = reply_result; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int do_quit(void) | ||||
| { | ||||
| 	daemon_reply reply; | ||||
| 	int rv = 0; | ||||
|  | ||||
| 	reply = daemon_send_simple(_lvmlockd, "quit", NULL); | ||||
|  | ||||
| 	if (reply.error) { | ||||
| 		log_error("reply error %d", reply.error); | ||||
| 		rv = reply.error; | ||||
| 	} | ||||
|  | ||||
| 	daemon_reply_destroy(reply); | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| static int setup_dump_socket(void) | ||||
| { | ||||
| 	int s, rv; | ||||
|  | ||||
| 	s = socket(AF_LOCAL, SOCK_DGRAM, 0); | ||||
| 	if (s < 0) | ||||
| 		return s; | ||||
|  | ||||
| 	memset(&dump_addr, 0, sizeof(dump_addr)); | ||||
| 	dump_addr.sun_family = AF_LOCAL; | ||||
| 	strcpy(&dump_addr.sun_path[1], DUMP_SOCKET_NAME); | ||||
| 	dump_addrlen = sizeof(sa_family_t) + strlen(dump_addr.sun_path+1) + 1; | ||||
|  | ||||
| 	rv = bind(s, (struct sockaddr *) &dump_addr, dump_addrlen); | ||||
| 	if (rv < 0) { | ||||
| 		if (!close(s)) | ||||
| 			log_error("failed to close dump socket"); | ||||
| 		return rv; | ||||
| 	} | ||||
|  | ||||
| 	return s; | ||||
| } | ||||
|  | ||||
| static int do_dump(const char *req_name) | ||||
| { | ||||
| 	daemon_reply reply; | ||||
| 	int result; | ||||
| 	int fd, rv = 0; | ||||
|  | ||||
| 	fd = setup_dump_socket(); | ||||
| 	if (fd < 0) { | ||||
| 		log_error("socket error %d", fd); | ||||
| 		return fd; | ||||
| 	} | ||||
|  | ||||
| 	reply = daemon_send_simple(_lvmlockd, req_name, NULL); | ||||
|  | ||||
| 	if (reply.error) { | ||||
| 		log_error("reply error %d", reply.error); | ||||
| 		rv = reply.error; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	result = daemon_reply_int(reply, "result", 0); | ||||
| 	dump_len = daemon_reply_int(reply, "dump_len", 0); | ||||
|  | ||||
| 	daemon_reply_destroy(reply); | ||||
|  | ||||
| 	if (result < 0) { | ||||
| 		rv = result; | ||||
| 		log_error("result %d", result); | ||||
| 	} | ||||
|  | ||||
| 	if (!dump_len) | ||||
| 		goto out; | ||||
|  | ||||
| 	memset(dump_buf, 0, sizeof(dump_buf)); | ||||
|  | ||||
| 	rv = recvfrom(fd, dump_buf, dump_len, MSG_WAITALL, | ||||
| 		      (struct sockaddr *)&dump_addr, &dump_addrlen); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("recvfrom error %d %d", rv, errno); | ||||
| 		rv = -errno; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	rv = 0; | ||||
| 	if ((info && dump) || !strcmp(req_name, "dump")) | ||||
| 		printf("%s\n", dump_buf); | ||||
| 	else | ||||
| 		format_info(); | ||||
| out: | ||||
| 	if (close(fd)) | ||||
| 		log_error("failed to close dump socket %d", fd); | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| static int do_able(const char *req_name) | ||||
| { | ||||
| 	daemon_reply reply; | ||||
| 	int result; | ||||
| 	int rv; | ||||
|  | ||||
| 	reply = _lvmlockd_send(req_name, | ||||
| 				"cmd = %s", "lvmlockctl", | ||||
| 				"pid = %d", getpid(), | ||||
| 				"vg_name = %s", arg_vg_name, | ||||
| 				NULL); | ||||
|  | ||||
| 	if (!_lvmlockd_result(reply, &result)) { | ||||
| 		log_error("lvmlockd result %d", result); | ||||
| 		rv = result; | ||||
| 	} else { | ||||
| 		rv = 0; | ||||
| 	} | ||||
|  | ||||
| 	daemon_reply_destroy(reply); | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| static int do_stop_lockspaces(void) | ||||
| { | ||||
| 	daemon_reply reply; | ||||
| 	char opts[32]; | ||||
| 	int result; | ||||
| 	int rv; | ||||
|  | ||||
| 	memset(opts, 0, sizeof(opts)); | ||||
|  | ||||
| 	if (wait_opt) | ||||
| 		strcat(opts, "wait "); | ||||
| 	if (force_opt) | ||||
| 		strcat(opts, "force "); | ||||
|  | ||||
| 	reply = _lvmlockd_send("stop_all", | ||||
| 				"cmd = %s", "lvmlockctl", | ||||
| 				"pid = %d", getpid(), | ||||
| 				"opts = %s", opts[0] ? opts : "none", | ||||
| 				NULL); | ||||
|  | ||||
| 	if (!_lvmlockd_result(reply, &result)) { | ||||
| 		log_error("lvmlockd result %d", result); | ||||
| 		rv = result; | ||||
| 	} else { | ||||
| 		rv = 0; | ||||
| 	} | ||||
|  | ||||
| 	daemon_reply_destroy(reply); | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| static int do_kill(void) | ||||
| { | ||||
| 	daemon_reply reply; | ||||
| 	int result; | ||||
| 	int rv; | ||||
|  | ||||
| 	syslog(LOG_EMERG, "Lost access to sanlock lease storage in VG %s.", arg_vg_name); | ||||
| 	/* These two lines explain the manual alternative to the FIXME below. */ | ||||
| 	syslog(LOG_EMERG, "Immediately deactivate LVs in VG %s.", arg_vg_name); | ||||
| 	syslog(LOG_EMERG, "Once VG is unused, run lvmlockctl --drop %s.", arg_vg_name); | ||||
|  | ||||
| 	/* | ||||
| 	 * It may not be strictly necessary to notify lvmlockd of the kill, but | ||||
| 	 * lvmlockd can use this information to avoid attempting any new lock | ||||
| 	 * requests in the VG (which would fail anyway), and can return an | ||||
| 	 * error indicating that the VG has been killed. | ||||
| 	 */ | ||||
|  | ||||
| 	reply = _lvmlockd_send("kill_vg", | ||||
| 				"cmd = %s", "lvmlockctl", | ||||
| 				"pid = %d", getpid(), | ||||
| 				"vg_name = %s", arg_vg_name, | ||||
| 				NULL); | ||||
|  | ||||
| 	if (!_lvmlockd_result(reply, &result)) { | ||||
| 		log_error("lvmlockd result %d", result); | ||||
| 		rv = result; | ||||
| 	} else { | ||||
| 		rv = 0; | ||||
| 	} | ||||
|  | ||||
| 	daemon_reply_destroy(reply); | ||||
|  | ||||
| 	/* | ||||
| 	 * FIXME: here is where we should implement a strong form of | ||||
| 	 * blkdeactivate, and if it completes successfully, automatically call | ||||
| 	 * do_drop() afterward.  (The drop step may not always be necessary | ||||
| 	 * if the lvm commands run while shutting things down release all the | ||||
| 	 * leases.) | ||||
| 	 * | ||||
| 	 * run_strong_blkdeactivate(); | ||||
| 	 * do_drop(); | ||||
| 	 */ | ||||
|  | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| static int do_drop(void) | ||||
| { | ||||
| 	daemon_reply reply; | ||||
| 	int result; | ||||
| 	int rv; | ||||
|  | ||||
| 	syslog(LOG_WARNING, "Dropping locks for VG %s.", arg_vg_name); | ||||
|  | ||||
| 	/* | ||||
| 	 * Check for misuse by looking for any active LVs in the VG | ||||
| 	 * and refusing this operation if found?  One possible way | ||||
| 	 * to kill LVs (e.g. if fs cannot be unmounted) is to suspend | ||||
| 	 * them, or replace them with the error target.  In that | ||||
| 	 * case the LV will still appear to be active, but it is | ||||
| 	 * safe to release the lock. | ||||
| 	 */ | ||||
|  | ||||
| 	reply = _lvmlockd_send("drop_vg", | ||||
| 				"cmd = %s", "lvmlockctl", | ||||
| 				"pid = %d", getpid(), | ||||
| 				"vg_name = %s", arg_vg_name, | ||||
| 				NULL); | ||||
|  | ||||
| 	if (!_lvmlockd_result(reply, &result)) { | ||||
| 		log_error("lvmlockd result %d", result); | ||||
| 		rv = result; | ||||
| 	} else { | ||||
| 		rv = 0; | ||||
| 	} | ||||
|  | ||||
| 	daemon_reply_destroy(reply); | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| static void print_usage(void) | ||||
| { | ||||
| 	printf("lvmlockctl options\n"); | ||||
| 	printf("Options:\n"); | ||||
| 	printf("--help | -h\n"); | ||||
| 	printf("      Show this help information.\n"); | ||||
| 	printf("--quit | -q\n"); | ||||
| 	printf("      Tell lvmlockd to quit.\n"); | ||||
| 	printf("--info | -i\n"); | ||||
| 	printf("      Print lock state information from lvmlockd.\n"); | ||||
| 	printf("--dump | -d\n"); | ||||
| 	printf("      Print log buffer from lvmlockd.\n"); | ||||
| 	printf("--wait | -w 0|1\n"); | ||||
| 	printf("      Wait option for other commands.\n"); | ||||
| 	printf("--force | -f 0|1>\n"); | ||||
| 	printf("      Force option for other commands.\n"); | ||||
| 	printf("--kill | -k <vg_name>\n"); | ||||
| 	printf("      Kill access to the vg when sanlock cannot renew lease.\n"); | ||||
| 	printf("--drop | -r <vg_name>\n"); | ||||
| 	printf("      Clear locks for the vg after it has been killed and is no longer used.\n"); | ||||
| 	printf("--gl-enable <vg_name>\n"); | ||||
| 	printf("      Tell lvmlockd to enable the global lock in a sanlock vg.\n"); | ||||
| 	printf("--gl-disable <vg_name>\n"); | ||||
| 	printf("      Tell lvmlockd to disable the global lock in a sanlock vg.\n"); | ||||
| 	printf("--stop-lockspaces | -S\n"); | ||||
| 	printf("      Stop all lockspaces.\n"); | ||||
| } | ||||
|  | ||||
| static int read_options(int argc, char *argv[]) | ||||
| { | ||||
| 	int option_index = 0; | ||||
| 	int c; | ||||
|  | ||||
| 	static struct option long_options[] = { | ||||
| 		{"help",            no_argument,       0,  'h' }, | ||||
| 		{"quit",            no_argument,       0,  'q' }, | ||||
| 		{"info",            no_argument,       0,  'i' }, | ||||
| 		{"dump",            no_argument,       0,  'd' }, | ||||
| 		{"wait",            required_argument, 0,  'w' }, | ||||
| 		{"force",           required_argument, 0,  'f' }, | ||||
| 		{"kill",            required_argument, 0,  'k' }, | ||||
| 		{"drop",            required_argument, 0,  'r' }, | ||||
| 		{"gl-enable",       required_argument, 0,  'E' }, | ||||
| 		{"gl-disable",      required_argument, 0,  'D' }, | ||||
| 		{"stop-lockspaces", no_argument,       0,  'S' }, | ||||
| 		{0, 0, 0, 0 } | ||||
| 	}; | ||||
|  | ||||
| 	if (argc == 1) { | ||||
| 		print_usage(); | ||||
| 		exit(0); | ||||
| 	} | ||||
|  | ||||
| 	while (1) { | ||||
| 		c = getopt_long(argc, argv, "hqidE:D:w:k:r:S", long_options, &option_index); | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
|  | ||||
| 		switch (c) { | ||||
| 		case 'h': | ||||
| 			/* --help */ | ||||
| 			print_usage(); | ||||
| 			exit(0); | ||||
| 		case 'q': | ||||
| 			/* --quit */ | ||||
| 			quit = 1; | ||||
| 			break; | ||||
| 		case 'i': | ||||
| 			/* --info */ | ||||
| 			info = 1; | ||||
| 			break; | ||||
| 		case 'd': | ||||
| 			/* --dump */ | ||||
| 			dump = 1; | ||||
| 			break; | ||||
| 		case 'w': | ||||
| 			wait_opt = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'k': | ||||
| 			kill_vg = 1; | ||||
| 			arg_vg_name = strdup(optarg); | ||||
| 			break; | ||||
| 		case 'r': | ||||
| 			drop_vg = 1; | ||||
| 			arg_vg_name = strdup(optarg); | ||||
| 			break; | ||||
| 		case 'E': | ||||
| 			gl_enable = 1; | ||||
| 			arg_vg_name = strdup(optarg); | ||||
| 			break; | ||||
| 		case 'D': | ||||
| 			gl_disable = 1; | ||||
| 			arg_vg_name = strdup(optarg); | ||||
| 			break; | ||||
| 		case 'S': | ||||
| 			stop_lockspaces = 1; | ||||
| 			break; | ||||
| 		default: | ||||
| 			print_usage(); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	int rv = 0; | ||||
|  | ||||
| 	rv = read_options(argc, argv); | ||||
| 	if (rv < 0) | ||||
| 		return rv; | ||||
|  | ||||
| 	_lvmlockd = lvmlockd_open(NULL); | ||||
|  | ||||
| 	if (_lvmlockd.socket_fd < 0 || _lvmlockd.error) { | ||||
| 		log_error("Cannot connect to lvmlockd."); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (quit) { | ||||
| 		rv = do_quit(); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (info) { | ||||
| 		rv = do_dump("info"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (dump) { | ||||
| 		rv = do_dump("dump"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (kill_vg) { | ||||
| 		rv = do_kill(); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (drop_vg) { | ||||
| 		rv = do_drop(); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (gl_enable) { | ||||
| 		rv = do_able("enable_gl"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (gl_disable) { | ||||
| 		rv = do_able("disable_gl"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (stop_lockspaces) { | ||||
| 		rv = do_stop_lockspaces(); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	lvmlockd_close(_lvmlockd); | ||||
| 	return rv; | ||||
| } | ||||
| @@ -1,51 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014-2015 Red Hat, Inc. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU Lesser General Public License v.2.1. | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_LVMLOCKD_CLIENT_H | ||||
| #define _LVM_LVMLOCKD_CLIENT_H | ||||
|  | ||||
| #include "daemon-client.h" | ||||
|  | ||||
| #define LVMLOCKD_SOCKET DEFAULT_RUN_DIR "/lvmlockd.socket" | ||||
|  | ||||
| /* Wrappers to open/close connection */ | ||||
|  | ||||
| static inline daemon_handle lvmlockd_open(const char *sock) | ||||
| { | ||||
| 	daemon_info lvmlockd_info = { | ||||
| 		.path = "lvmlockd", | ||||
| 		.socket = sock ?: LVMLOCKD_SOCKET, | ||||
| 		.protocol = "lvmlockd", | ||||
| 		.protocol_version = 1, | ||||
| 		.autostart = 0 | ||||
| 	}; | ||||
|  | ||||
| 	return daemon_open(lvmlockd_info); | ||||
| } | ||||
|  | ||||
| static inline void lvmlockd_close(daemon_handle h) | ||||
| { | ||||
| 	return daemon_close(h); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Errors returned as the lvmlockd result value. | ||||
|  */ | ||||
| #define ENOLS     210 /* lockspace not found */ | ||||
| #define ESTARTING 211 /* lockspace is starting */ | ||||
| #define EARGS     212 | ||||
| #define EHOSTID   213 | ||||
| #define EMANAGER  214 | ||||
| #define EPREPARE  215 | ||||
| #define ELOCKD    216 | ||||
| #define EVGKILLED 217 /* sanlock lost access to leases and VG is killed. */ | ||||
| #define ELOCKIO   218 /* sanlock io errors during lock op, may be transient. */ | ||||
|  | ||||
| #endif	/* _LVM_LVMLOCKD_CLIENT_H */ | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,662 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014-2015 Red Hat, Inc. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU Lesser General Public License v.2.1. | ||||
|  */ | ||||
|  | ||||
| #define _XOPEN_SOURCE 500  /* pthread */ | ||||
| #define _ISOC99_SOURCE | ||||
|  | ||||
| #include "tool.h" | ||||
|  | ||||
| #include "daemon-server.h" | ||||
| #include "daemon-log.h" | ||||
| #include "xlate.h" | ||||
|  | ||||
| #include "lvmlockd-internal.h" | ||||
| #include "lvmlockd-client.h" | ||||
|  | ||||
| /* | ||||
|  * Using synchronous _wait dlm apis so do not define _REENTRANT and | ||||
|  * link with non-threaded version of library, libdlm_lt. | ||||
|  */ | ||||
| #include "libdlm.h" | ||||
|  | ||||
| #include <pthread.h> | ||||
| #include <stddef.h> | ||||
| #include <poll.h> | ||||
| #include <errno.h> | ||||
| #include <endian.h> | ||||
| #include <fcntl.h> | ||||
| #include <byteswap.h> | ||||
| #include <syslog.h> | ||||
| #include <dirent.h> | ||||
| #include <sys/socket.h> | ||||
|  | ||||
| struct lm_dlm { | ||||
| 	dlm_lshandle_t *dh; | ||||
| }; | ||||
|  | ||||
| struct rd_dlm { | ||||
| 	struct dlm_lksb lksb; | ||||
| 	struct val_blk *vb; | ||||
| }; | ||||
|  | ||||
| int lm_data_size_dlm(void) | ||||
| { | ||||
| 	return sizeof(struct rd_dlm); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * lock_args format | ||||
|  * | ||||
|  * vg_lock_args format for dlm is | ||||
|  * vg_version_string:undefined:cluster_name | ||||
|  * | ||||
|  * lv_lock_args are not used for dlm | ||||
|  * | ||||
|  * version_string is MAJOR.MINOR.PATCH | ||||
|  * undefined may contain ":" | ||||
|  */ | ||||
|  | ||||
| #define VG_LOCK_ARGS_MAJOR 1 | ||||
| #define VG_LOCK_ARGS_MINOR 0 | ||||
| #define VG_LOCK_ARGS_PATCH 0 | ||||
|  | ||||
| static int cluster_name_from_args(char *vg_args, char *clustername) | ||||
| { | ||||
| 	return last_string_from_args(vg_args, clustername); | ||||
| } | ||||
|  | ||||
| static int check_args_version(char *vg_args) | ||||
| { | ||||
| 	unsigned int major = 0; | ||||
| 	int rv; | ||||
|  | ||||
| 	rv = version_from_args(vg_args, &major, NULL, NULL); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("check_args_version %s error %d", vg_args, rv); | ||||
| 		return rv; | ||||
| 	} | ||||
|  | ||||
| 	if (major > VG_LOCK_ARGS_MAJOR) { | ||||
| 		log_error("check_args_version %s major %d %d", vg_args, major, VG_LOCK_ARGS_MAJOR); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* This will be set after dlm_controld is started. */ | ||||
| #define DLM_CLUSTER_NAME_PATH "/sys/kernel/config/dlm/cluster/cluster_name" | ||||
|  | ||||
| static int read_cluster_name(char *clustername) | ||||
| { | ||||
| 	static const char close_error_msg[] = "read_cluster_name: close_error %d"; | ||||
| 	char *n; | ||||
| 	int fd; | ||||
| 	int rv; | ||||
|  | ||||
| 	if (daemon_test) { | ||||
| 		sprintf(clustername, "%s", "test"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	fd = open(DLM_CLUSTER_NAME_PATH, O_RDONLY); | ||||
| 	if (fd < 0) { | ||||
| 		log_debug("read_cluster_name: open error %d, check dlm_controld", fd); | ||||
| 		return fd; | ||||
| 	} | ||||
|  | ||||
| 	rv = read(fd, clustername, MAX_ARGS); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("read_cluster_name: cluster name read error %d, check dlm_controld", fd); | ||||
| 		if (close(fd)) | ||||
| 			log_error(close_error_msg, fd); | ||||
| 		return rv; | ||||
| 	} | ||||
|  | ||||
| 	n = strstr(clustername, "\n"); | ||||
| 	if (n) | ||||
| 		*n = '\0'; | ||||
| 	if (close(fd)) | ||||
| 		log_error(close_error_msg, fd); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lm_init_vg_dlm(char *ls_name, char *vg_name, uint32_t flags, char *vg_args) | ||||
| { | ||||
| 	char clustername[MAX_ARGS+1]; | ||||
| 	char lock_args_version[MAX_ARGS+1]; | ||||
| 	int rv; | ||||
|  | ||||
| 	memset(clustername, 0, sizeof(clustername)); | ||||
| 	memset(lock_args_version, 0, sizeof(lock_args_version)); | ||||
|  | ||||
| 	snprintf(lock_args_version, MAX_ARGS, "%u.%u.%u", | ||||
| 		 VG_LOCK_ARGS_MAJOR, VG_LOCK_ARGS_MINOR, VG_LOCK_ARGS_PATCH); | ||||
|  | ||||
| 	rv = read_cluster_name(clustername); | ||||
| 	if (rv < 0) | ||||
| 		return -EMANAGER; | ||||
|  | ||||
| 	if (strlen(clustername) + strlen(lock_args_version) + 2 > MAX_ARGS) { | ||||
| 		log_error("init_vg_dlm args too long"); | ||||
| 		return -EARGS; | ||||
| 	} | ||||
|  | ||||
| 	snprintf(vg_args, MAX_ARGS, "%s:%s", lock_args_version, clustername); | ||||
| 	rv = 0; | ||||
|  | ||||
| 	log_debug("init_vg_dlm done %s vg_args %s", ls_name, vg_args); | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| int lm_prepare_lockspace_dlm(struct lockspace *ls) | ||||
| { | ||||
| 	char sys_clustername[MAX_ARGS+1]; | ||||
| 	char arg_clustername[MAX_ARGS+1]; | ||||
| 	struct lm_dlm *lmd; | ||||
| 	int rv; | ||||
|  | ||||
| 	memset(sys_clustername, 0, sizeof(sys_clustername)); | ||||
| 	memset(arg_clustername, 0, sizeof(arg_clustername)); | ||||
|  | ||||
| 	rv = read_cluster_name(sys_clustername); | ||||
| 	if (rv < 0) | ||||
| 		return -EMANAGER; | ||||
|  | ||||
| 	if (!ls->vg_args[0]) { | ||||
| 		/* global lockspace has no vg args */ | ||||
| 		goto skip_args; | ||||
| 	} | ||||
|  | ||||
| 	rv = check_args_version(ls->vg_args); | ||||
| 	if (rv < 0) | ||||
| 		return -EARGS; | ||||
|  | ||||
| 	rv = cluster_name_from_args(ls->vg_args, arg_clustername); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("prepare_lockspace_dlm %s no cluster name from args %s", ls->name, ls->vg_args); | ||||
| 		return -EARGS; | ||||
| 	} | ||||
|  | ||||
| 	if (strcmp(sys_clustername, arg_clustername)) { | ||||
| 		log_error("prepare_lockspace_dlm %s mismatching cluster names sys %s arg %s", | ||||
| 			  ls->name, sys_clustername, arg_clustername); | ||||
| 		return -EARGS; | ||||
| 	} | ||||
|  | ||||
|  skip_args: | ||||
| 	lmd = malloc(sizeof(struct lm_dlm)); | ||||
| 	if (!lmd) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	ls->lm_data = lmd; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lm_add_lockspace_dlm(struct lockspace *ls, int adopt) | ||||
| { | ||||
| 	struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data; | ||||
|  | ||||
| 	if (daemon_test) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (adopt) | ||||
| 		lmd->dh = dlm_open_lockspace(ls->name); | ||||
| 	else | ||||
| 		lmd->dh = dlm_new_lockspace(ls->name, 0600, DLM_LSFL_NEWEXCL); | ||||
|  | ||||
| 	if (!lmd->dh) { | ||||
| 		log_error("add_lockspace_dlm %s adopt %d error", ls->name, adopt); | ||||
| 		free(lmd); | ||||
| 		ls->lm_data = NULL; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lm_rem_lockspace_dlm(struct lockspace *ls, int free_vg) | ||||
| { | ||||
| 	struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data; | ||||
| 	int rv; | ||||
|  | ||||
| 	if (daemon_test) | ||||
| 		goto out; | ||||
|  | ||||
| 	/* | ||||
| 	 * If free_vg is set, it means we are doing vgremove, and we may want | ||||
| 	 * to tell any other nodes to leave the lockspace.  This is not really | ||||
| 	 * necessary since there should be no harm in having an unused | ||||
| 	 * lockspace sitting around.  A new "notification lock" would need to | ||||
| 	 * be added with a callback to signal this.  | ||||
| 	 */ | ||||
|  | ||||
| 	rv = dlm_release_lockspace(ls->name, lmd->dh, 1); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("rem_lockspace_dlm error %d", rv); | ||||
| 		return rv; | ||||
| 	} | ||||
|  out: | ||||
| 	free(lmd); | ||||
| 	ls->lm_data = NULL; | ||||
|  | ||||
| 	if (!strcmp(ls->name, gl_lsname_dlm)) | ||||
| 		gl_running_dlm = 0; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int lm_add_resource_dlm(struct lockspace *ls, struct resource *r, int with_lock_nl) | ||||
| { | ||||
| 	struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data; | ||||
| 	struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data; | ||||
| 	uint32_t flags = 0; | ||||
| 	char *buf; | ||||
| 	int rv; | ||||
|  | ||||
| 	if (r->type == LD_RT_GL || r->type == LD_RT_VG) { | ||||
| 		buf = malloc(sizeof(struct val_blk) + DLM_LVB_LEN); | ||||
| 		if (!buf) | ||||
| 			return -ENOMEM; | ||||
| 		memset(buf, 0, sizeof(struct val_blk) + DLM_LVB_LEN); | ||||
|  | ||||
| 		rdd->vb = (struct val_blk *)buf; | ||||
| 		rdd->lksb.sb_lvbptr = buf + sizeof(struct val_blk); | ||||
|  | ||||
| 		flags |= LKF_VALBLK; | ||||
| 	} | ||||
|  | ||||
| 	if (!with_lock_nl) | ||||
| 		goto out; | ||||
|  | ||||
| 	/* because this is a new NL lock request */ | ||||
| 	flags |= LKF_EXPEDITE; | ||||
|  | ||||
| 	if (daemon_test) | ||||
| 		goto out; | ||||
|  | ||||
| 	rv = dlm_ls_lock_wait(lmd->dh, LKM_NLMODE, &rdd->lksb, flags, | ||||
| 			      r->name, strlen(r->name), | ||||
| 			      0, NULL, NULL, NULL); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s add_resource_dlm lock error %d", ls->name, r->name, rv); | ||||
| 		return rv; | ||||
| 	} | ||||
|  out: | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lm_rem_resource_dlm(struct lockspace *ls, struct resource *r) | ||||
| { | ||||
| 	struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data; | ||||
| 	struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data; | ||||
| 	struct dlm_lksb *lksb; | ||||
| 	int rv = 0; | ||||
|  | ||||
| 	if (daemon_test) | ||||
| 		goto out; | ||||
|  | ||||
| 	lksb = &rdd->lksb; | ||||
|  | ||||
| 	if (!lksb->sb_lkid) | ||||
| 		goto out; | ||||
|  | ||||
| 	rv = dlm_ls_unlock_wait(lmd->dh, lksb->sb_lkid, 0, lksb); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s rem_resource_dlm unlock error %d", ls->name, r->name, rv); | ||||
| 	} | ||||
|  out: | ||||
| 	if (rdd->vb) | ||||
| 		free(rdd->vb); | ||||
|  | ||||
| 	memset(rdd, 0, sizeof(struct rd_dlm)); | ||||
| 	r->lm_init = 0; | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| static int to_dlm_mode(int ld_mode) | ||||
| { | ||||
| 	switch (ld_mode) { | ||||
| 	case LD_LK_EX: | ||||
| 		return LKM_EXMODE; | ||||
| 	case LD_LK_SH: | ||||
| 		return LKM_PRMODE; | ||||
| 	}; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static int lm_adopt_dlm(struct lockspace *ls, struct resource *r, int ld_mode, | ||||
| 			uint32_t *r_version) | ||||
| { | ||||
| 	struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data; | ||||
| 	struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data; | ||||
| 	struct dlm_lksb *lksb; | ||||
| 	uint32_t flags = 0; | ||||
| 	int mode; | ||||
| 	int rv; | ||||
|  | ||||
| 	*r_version = 0; | ||||
|  | ||||
| 	if (!r->lm_init) { | ||||
| 		rv = lm_add_resource_dlm(ls, r, 0); | ||||
| 		if (rv < 0) | ||||
| 			return rv; | ||||
| 		r->lm_init = 1; | ||||
| 	} | ||||
|  | ||||
| 	lksb = &rdd->lksb; | ||||
|  | ||||
| 	flags |= LKF_PERSISTENT; | ||||
| 	flags |= LKF_ORPHAN; | ||||
|  | ||||
| 	if (rdd->vb) | ||||
| 		flags |= LKF_VALBLK; | ||||
|  | ||||
| 	mode = to_dlm_mode(ld_mode); | ||||
| 	if (mode < 0) { | ||||
| 		log_error("adopt_dlm invalid mode %d", ld_mode); | ||||
| 		rv = -EINVAL; | ||||
| 		goto fail; | ||||
| 	} | ||||
|  | ||||
| 	log_debug("S %s R %s adopt_dlm", ls->name, r->name); | ||||
|  | ||||
| 	if (daemon_test) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* | ||||
| 	 * dlm returns 0 for success, -EAGAIN if an orphan is | ||||
| 	 * found with another mode, and -ENOENT if no orphan. | ||||
| 	 * | ||||
| 	 * cast/bast/param are (void *)1 because the kernel | ||||
| 	 * returns errors if some are null. | ||||
| 	 */ | ||||
|  | ||||
| 	rv = dlm_ls_lockx(lmd->dh, mode, lksb, flags, | ||||
| 			  r->name, strlen(r->name), 0, | ||||
| 			  (void *)1, (void *)1, (void *)1, | ||||
| 			  NULL, NULL); | ||||
|  | ||||
| 	if (rv == -EAGAIN) { | ||||
| 		log_debug("S %s R %s adopt_dlm adopt mode %d try other mode", | ||||
| 			  ls->name, r->name, ld_mode); | ||||
| 		rv = -EUCLEAN; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 	if (rv < 0) { | ||||
| 		log_debug("S %s R %s adopt_dlm mode %d flags %x error %d errno %d", | ||||
| 			  ls->name, r->name, mode, flags, rv, errno); | ||||
| 		goto fail; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * FIXME: For GL/VG locks we probably want to read the lvb, | ||||
| 	 * especially if adopting an ex lock, because when we | ||||
| 	 * release this adopted ex lock we may want to write new | ||||
| 	 * lvb values based on the current lvb values (at lease | ||||
| 	 * in the GL case where we increment the current values.) | ||||
| 	 * | ||||
| 	 * It should be possible to read the lvb by requesting | ||||
| 	 * this lock in the same mode it's already in. | ||||
| 	 */ | ||||
|  | ||||
| 	return rv; | ||||
|  | ||||
|  fail: | ||||
| 	lm_rem_resource_dlm(ls, r); | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Use PERSISTENT so that if lvmlockd exits while holding locks, | ||||
|  * the locks will remain orphaned in the dlm, still protecting what | ||||
|  * they were acquired to protect. | ||||
|  */ | ||||
|  | ||||
| int lm_lock_dlm(struct lockspace *ls, struct resource *r, int ld_mode, | ||||
| 		uint32_t *r_version, int adopt) | ||||
| { | ||||
| 	struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data; | ||||
| 	struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data; | ||||
| 	struct dlm_lksb *lksb; | ||||
| 	struct val_blk vb; | ||||
| 	uint32_t flags = 0; | ||||
| 	uint16_t vb_version; | ||||
| 	int mode; | ||||
| 	int rv; | ||||
|  | ||||
| 	if (adopt) { | ||||
| 		/* When adopting, we don't follow the normal method | ||||
| 		   of acquiring a NL lock then converting it to the | ||||
| 		   desired mode. */ | ||||
| 		return lm_adopt_dlm(ls, r, ld_mode, r_version); | ||||
| 	} | ||||
|  | ||||
| 	if (!r->lm_init) { | ||||
| 		rv = lm_add_resource_dlm(ls, r, 1); | ||||
| 		if (rv < 0) | ||||
| 			return rv; | ||||
| 		r->lm_init = 1; | ||||
| 	} | ||||
|  | ||||
| 	lksb = &rdd->lksb; | ||||
|  | ||||
| 	flags |= LKF_CONVERT; | ||||
| 	flags |= LKF_NOQUEUE; | ||||
| 	flags |= LKF_PERSISTENT; | ||||
|  | ||||
| 	if (rdd->vb) | ||||
| 		flags |= LKF_VALBLK; | ||||
|  | ||||
| 	mode = to_dlm_mode(ld_mode); | ||||
| 	if (mode < 0) { | ||||
| 		log_error("lock_dlm invalid mode %d", ld_mode); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	log_debug("S %s R %s lock_dlm", ls->name, r->name); | ||||
|  | ||||
| 	if (daemon_test) { | ||||
| 		*r_version = 0; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	rv = dlm_ls_lock_wait(lmd->dh, mode, lksb, flags, | ||||
| 			      r->name, strlen(r->name), | ||||
| 			      0, NULL, NULL, NULL); | ||||
| 	if (rv == -EAGAIN) { | ||||
| 		log_error("S %s R %s lock_dlm mode %d rv EAGAIN", ls->name, r->name, mode); | ||||
| 		return -EAGAIN; | ||||
| 	} | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s lock_dlm error %d", ls->name, r->name, rv); | ||||
| 		return rv; | ||||
| 	} | ||||
|  | ||||
| 	if (rdd->vb) { | ||||
| 		if (lksb->sb_flags & DLM_SBF_VALNOTVALID) { | ||||
| 			log_debug("S %s R %s lock_dlm VALNOTVALID", ls->name, r->name); | ||||
| 			memset(rdd->vb, 0, sizeof(struct val_blk)); | ||||
| 			*r_version = 0; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		memcpy(&vb, lksb->sb_lvbptr, sizeof(struct val_blk)); | ||||
| 		vb_version = le16_to_cpu(vb.version); | ||||
|  | ||||
| 		if (vb_version && ((vb_version & 0xFF00) > (VAL_BLK_VERSION & 0xFF00))) { | ||||
| 			log_error("S %s R %s lock_dlm ignore vb_version %x", | ||||
| 				  ls->name, r->name, vb_version); | ||||
| 			*r_version = 0; | ||||
| 			free(rdd->vb); | ||||
| 			rdd->vb = NULL; | ||||
| 			lksb->sb_lvbptr = NULL; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		*r_version = le32_to_cpu(vb.r_version); | ||||
| 		memcpy(rdd->vb, &vb, sizeof(vb)); /* rdd->vb saved as le */ | ||||
|  | ||||
| 		log_debug("S %s R %s lock_dlm get r_version %u", | ||||
| 			  ls->name, r->name, *r_version); | ||||
| 	} | ||||
| out: | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lm_convert_dlm(struct lockspace *ls, struct resource *r, | ||||
| 		   int ld_mode, uint32_t r_version) | ||||
| { | ||||
| 	struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data; | ||||
| 	struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data; | ||||
| 	struct dlm_lksb *lksb = &rdd->lksb; | ||||
| 	uint32_t mode; | ||||
| 	uint32_t flags = 0; | ||||
| 	int rv; | ||||
|  | ||||
| 	log_debug("S %s R %s convert_dlm", ls->name, r->name); | ||||
|  | ||||
| 	flags |= LKF_CONVERT; | ||||
| 	flags |= LKF_NOQUEUE; | ||||
| 	flags |= LKF_PERSISTENT; | ||||
|  | ||||
| 	if (rdd->vb && r_version && (r->mode == LD_LK_EX)) { | ||||
| 		if (!rdd->vb->version) { | ||||
| 			/* first time vb has been written */ | ||||
| 			rdd->vb->version = cpu_to_le16(VAL_BLK_VERSION); | ||||
| 		} | ||||
| 		rdd->vb->r_version = cpu_to_le32(r_version); | ||||
| 		memcpy(lksb->sb_lvbptr, rdd->vb, sizeof(struct val_blk)); | ||||
|  | ||||
| 		log_debug("S %s R %s convert_dlm set r_version %u", | ||||
| 			  ls->name, r->name, r_version); | ||||
|  | ||||
| 		flags |= LKF_VALBLK; | ||||
| 	} | ||||
|  | ||||
| 	mode = to_dlm_mode(ld_mode); | ||||
|  | ||||
| 	if (daemon_test) | ||||
| 		return 0; | ||||
|  | ||||
| 	rv = dlm_ls_lock_wait(lmd->dh, mode, lksb, flags, | ||||
| 			      r->name, strlen(r->name), | ||||
| 			      0, NULL, NULL, NULL); | ||||
| 	if (rv == -EAGAIN) { | ||||
| 		/* FIXME: When does this happen?  Should something different be done? */ | ||||
| 		log_error("S %s R %s convert_dlm mode %d rv EAGAIN", ls->name, r->name, mode); | ||||
| 		return -EAGAIN; | ||||
| 	} | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s convert_dlm error %d", ls->name, r->name, rv); | ||||
| 	} | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| int lm_unlock_dlm(struct lockspace *ls, struct resource *r, | ||||
| 		  uint32_t r_version, uint32_t lmuf_flags) | ||||
| { | ||||
| 	struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data; | ||||
| 	struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data; | ||||
| 	struct dlm_lksb *lksb = &rdd->lksb; | ||||
| 	uint32_t flags = 0; | ||||
| 	int rv; | ||||
|  | ||||
| 	log_debug("S %s R %s unlock_dlm r_version %u flags %x", | ||||
| 		  ls->name, r->name, r_version, lmuf_flags); | ||||
|  | ||||
| 	/* | ||||
| 	 * Do not set PERSISTENT, because we don't need an orphan | ||||
| 	 * NL lock to protect anything. | ||||
| 	 */ | ||||
|  | ||||
| 	flags |= LKF_CONVERT; | ||||
|  | ||||
| 	if (rdd->vb && r_version && (r->mode == LD_LK_EX)) { | ||||
| 		if (!rdd->vb->version) { | ||||
| 			/* first time vb has been written */ | ||||
| 			rdd->vb->version = cpu_to_le16(VAL_BLK_VERSION); | ||||
| 		} | ||||
| 		if (r_version) | ||||
| 			rdd->vb->r_version = cpu_to_le32(r_version); | ||||
| 		memcpy(lksb->sb_lvbptr, rdd->vb, sizeof(struct val_blk)); | ||||
|  | ||||
| 		log_debug("S %s R %s unlock_dlm set r_version %u", | ||||
| 			  ls->name, r->name, r_version); | ||||
|  | ||||
| 		flags |= LKF_VALBLK; | ||||
| 	} | ||||
|  | ||||
| 	if (daemon_test) | ||||
| 		return 0; | ||||
|  | ||||
| 	rv = dlm_ls_lock_wait(lmd->dh, LKM_NLMODE, lksb, flags, | ||||
| 			      r->name, strlen(r->name), | ||||
| 			      0, NULL, NULL, NULL); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s unlock_dlm error %d", ls->name, r->name, rv); | ||||
| 	} | ||||
|  | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * This list could be read from dlm_controld via libdlmcontrol, | ||||
|  * but it's simpler to get it from sysfs. | ||||
|  */ | ||||
|  | ||||
| #define DLM_LOCKSPACES_PATH "/sys/kernel/config/dlm/cluster/spaces" | ||||
|  | ||||
| int lm_get_lockspaces_dlm(struct list_head *ls_rejoin) | ||||
| { | ||||
| 	static const char closedir_err_msg[] = "lm_get_lockspace_dlm: closedir failed"; | ||||
| 	struct lockspace *ls; | ||||
| 	struct dirent *de; | ||||
| 	DIR *ls_dir; | ||||
|  | ||||
| 	if (!(ls_dir = opendir(DLM_LOCKSPACES_PATH))) | ||||
| 		return -ECONNREFUSED; | ||||
|  | ||||
| 	while ((de = readdir(ls_dir))) { | ||||
| 		if (de->d_name[0] == '.') | ||||
| 			continue; | ||||
|  | ||||
| 		if (strncmp(de->d_name, LVM_LS_PREFIX, strlen(LVM_LS_PREFIX))) | ||||
| 			continue; | ||||
|  | ||||
| 		if (!(ls = alloc_lockspace())) { | ||||
| 			if (closedir(ls_dir)) | ||||
| 				log_error(closedir_err_msg); | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
|  | ||||
| 		ls->lm_type = LD_LM_DLM; | ||||
| 		strncpy(ls->name, de->d_name, MAX_NAME); | ||||
| 		strncpy(ls->vg_name, ls->name + strlen(LVM_LS_PREFIX), MAX_NAME); | ||||
| 		list_add_tail(&ls->list, ls_rejoin); | ||||
| 	} | ||||
|  | ||||
| 	if (closedir(ls_dir)) | ||||
| 		log_error(closedir_err_msg); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lm_is_running_dlm(void) | ||||
| { | ||||
| 	char sys_clustername[MAX_ARGS+1]; | ||||
| 	int rv; | ||||
|  | ||||
| 	memset(sys_clustername, 0, sizeof(sys_clustername)); | ||||
|  | ||||
| 	rv = read_cluster_name(sys_clustername); | ||||
| 	if (rv < 0) | ||||
| 		return 0; | ||||
| 	return 1; | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user