Compare commits
1219 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f65f793a0d | ||
|
f7ad489805 | ||
|
62459c4f00 | ||
|
05bd88b4d8 | ||
|
0546fff1ce | ||
|
83f7bd66e6 | ||
|
14ebb2fb93 | ||
|
917a0239a9 | ||
|
d0125d5aad | ||
|
65a3da92f1 | ||
|
5c621fa253 | ||
|
f9e71bee0a | ||
|
c3804877ed | ||
|
35e53ab9c7 | ||
|
053987b9e1 | ||
|
c0bc93c7c4 | ||
|
1f596eb2bc | ||
|
3f59b51be1 | ||
|
9d59da8974 | ||
|
51218bd1ad | ||
|
e1c3c53058 | ||
|
9fa497b056 | ||
|
057fbfc2b9 | ||
|
33156071cd | ||
|
bbb759c684 | ||
|
f1a7bc9ecd | ||
|
d514608f91 | ||
|
e8c2e26358 | ||
|
61c67849d7 | ||
|
2cdbb34763 | ||
|
538ba918c1 | ||
|
c330df9700 | ||
|
1789a2db29 | ||
|
c270b83341 | ||
|
02f0788033 | ||
|
a2ea6e8d47 | ||
|
a986f53134 | ||
|
9d2d45b473 | ||
|
0c892e6b3a | ||
|
a0fc38d6a2 | ||
|
28cbf45d1d | ||
|
72829be281 | ||
|
d57f9906bd | ||
|
3d4fea6510 | ||
|
f670644954 | ||
|
dd6821814a | ||
|
f33584511c | ||
|
3ec84553ef | ||
|
a301d4381f | ||
|
49b8ad7e94 | ||
|
180d29c7b3 | ||
|
a23ba91681 | ||
|
d986da63a5 | ||
|
d3ac8bb6f8 | ||
|
f87f3b9199 | ||
|
67bdcbcf19 | ||
|
69b2a5a876 | ||
|
a79dff3d98 | ||
|
c859a23136 | ||
|
7f92671765 | ||
|
ec38105b08 | ||
|
85dceb12a1 | ||
|
5126c37c1e | ||
|
e7e03e36de | ||
|
13c393fc9d | ||
|
cc2f375b13 | ||
|
49d5109c96 | ||
|
e5c8f5e2fa | ||
|
ff925a7578 | ||
|
33939a30ea | ||
|
308b296285 | ||
|
d55012f724 | ||
|
7c6eecc9c8 | ||
|
e4cbf5023a | ||
|
46356d3027 | ||
|
958a263437 | ||
|
a89ae82270 | ||
|
cedc39bf09 | ||
|
0131055ec9 | ||
|
0627e68078 | ||
|
7d78f6314a | ||
|
0f509f4a34 | ||
|
b043000044 | ||
|
c338eab3c6 | ||
|
24e1dc92ba | ||
|
00f98543ad | ||
|
8684356b44 | ||
|
a89615365e | ||
|
3c36c22ca8 | ||
|
7a2f5f43f9 | ||
|
dac27f1f18 | ||
|
6f276ae4bb | ||
|
537e288758 | ||
|
3cea1bd423 | ||
|
c976bbcfd2 | ||
|
ec29829550 | ||
|
cf7180af5b | ||
|
c58e3acc78 | ||
|
b78aef1e7e | ||
|
f20e4ddf3b | ||
|
40656641b6 | ||
|
6f8e8e98bb | ||
|
24b4aabae9 | ||
|
a9c6d79cda | ||
|
45bf4b02e7 | ||
|
667c5138cc | ||
|
85cf98888d | ||
|
048e5bb9a2 | ||
|
b9d47d0dd2 | ||
|
1813c25117 | ||
|
940c9c3bae | ||
|
5d0d7336ff | ||
|
6a26a05dbf | ||
|
c22e94c9c7 | ||
|
4aafa8cf47 | ||
|
9235a11d5f | ||
|
f141b9f778 | ||
|
1dca1efbc6 | ||
|
28c6b1393f | ||
|
6c97b8b875 | ||
|
ec2426b24d | ||
|
5cd3b11391 | ||
|
d30b565d98 | ||
|
74008d28a7 | ||
|
0d1b3cee4f | ||
|
0b44cb4426 | ||
|
eb8e4496b2 | ||
|
30f45d4df7 | ||
|
24e1c78d52 | ||
|
476fecbcf5 | ||
|
327b58fa03 | ||
|
d3eea9404d | ||
|
ca309d3b28 | ||
|
0ebd68f17f | ||
|
3696373609 | ||
|
165affc39b | ||
|
f794018e0c | ||
|
337cc5ca18 | ||
|
4e4d6b5d1f | ||
|
b011adc453 | ||
|
d8abc1d266 | ||
|
6cde066edd | ||
|
d163e38380 | ||
|
d9195970b7 | ||
|
20b946eae5 | ||
|
c00357663b | ||
|
2feb5f22cc | ||
|
f3aee056ad | ||
|
91ff5ca0ca | ||
|
4238e36d7f | ||
|
03a9d3bc5b | ||
|
fe46dee194 | ||
|
1c6c35f861 | ||
|
926b25fd54 | ||
|
e58142b4ac | ||
|
3e5c6afaee | ||
|
a1bf9fad98 | ||
|
afdc4219d0 | ||
|
94e4ed6644 | ||
|
ef8aaba014 | ||
|
d2ae07e69b | ||
|
760dd01b50 | ||
|
5bf90cb77d | ||
|
f5c3dc5d9d | ||
|
f2b820079a | ||
|
083024496a | ||
|
e01eea8ba8 | ||
|
eef948d797 | ||
|
8017be7d09 | ||
|
5b628a4c77 | ||
|
ddbdb51d20 | ||
|
0081a7ca92 | ||
|
e80ae4906f | ||
|
e4e22fb58e | ||
|
7086a400e8 | ||
|
a25a13fde0 | ||
|
993beab3b7 | ||
|
06875f2cc9 | ||
|
448fc7231c | ||
|
d0d030bafc | ||
|
833131e15f | ||
|
fb2810557a | ||
|
82a64db42b | ||
|
57af0ba0a4 | ||
|
17c1e64f7e | ||
|
af13ceac24 | ||
|
af2ec61965 | ||
|
a55cc55338 | ||
|
b2fb3fd35a | ||
|
94b7c8eb59 | ||
|
6d671007dd | ||
|
1a8d5fb4a9 | ||
|
f90c412b35 | ||
|
c73b5456eb | ||
|
3d87398721 | ||
|
96681887b8 | ||
|
f2c675cff6 | ||
|
1844bf17a7 | ||
|
47b56d6123 | ||
|
814d5f6d6c | ||
|
512d2dd5f8 | ||
|
ad20d5ac1c | ||
|
b79ff05e29 | ||
|
32b50f8188 | ||
|
a44096a26d | ||
|
32cf94fc74 | ||
|
39e28cd80a | ||
|
9b1356ad01 | ||
|
45521b377a | ||
|
46a366635c | ||
|
384b588a2d | ||
|
03e5a78d8f | ||
|
e32c76b059 | ||
|
ba7782f867 | ||
|
c5091b0e49 | ||
|
b54e32f0ca | ||
|
328e7738d5 | ||
|
aca359c2e2 | ||
|
1cd40dc9e5 | ||
|
dc9407281f | ||
|
8f727c1f35 | ||
|
a08ea28feb | ||
|
c5535dd9a0 | ||
|
3519cc5e44 | ||
|
d251bbbb05 | ||
|
1fc3ec9029 | ||
|
1877128fc4 | ||
|
76c5b4a06b | ||
|
9cd674b528 | ||
|
5aea7eb860 | ||
|
ec30df2020 | ||
|
bb33427c79 | ||
|
28a16cebda | ||
|
49c79405bc | ||
|
3098f32a07 | ||
|
b1e9c05bee | ||
|
69e06d89ff | ||
|
56b04017e7 | ||
|
3af61c031d | ||
|
f42a57088f | ||
|
4834452875 | ||
|
c5b9d99b48 | ||
|
f8d4c36f6d | ||
|
fa9ecbdb81 | ||
|
2bf6b29b8f | ||
|
25812f88f6 | ||
|
037989b8e4 | ||
|
dbc82cfb6a | ||
|
941a766aa3 | ||
|
72be7ad5f0 | ||
|
b2a36f71a0 | ||
|
6b17620389 | ||
|
b04195041a | ||
|
8ca4c66e3c | ||
|
449b7bf6e4 | ||
|
88e4b6390c | ||
|
de92bd1884 | ||
|
1b276a74fe | ||
|
fff31558a0 | ||
|
8084c4b2a3 | ||
|
1ca852191f | ||
|
af5fc6547a | ||
|
70f2b26c86 | ||
|
cfd95dbe87 | ||
|
c5bb404d6a | ||
|
4c23964964 | ||
|
e4a6dcd35c | ||
|
d4addd53ad | ||
|
d21298c156 | ||
|
9dba9fb366 | ||
|
09d40679c0 | ||
|
a323b126e5 | ||
|
553aad6ed2 | ||
|
5ca2ad6148 | ||
|
40537e1522 | ||
|
064a75b21b | ||
|
1d3445bc0f | ||
|
79232d02c9 | ||
|
9bd4986781 | ||
|
c3d443aaff | ||
|
b087888f94 | ||
|
da3dfd0998 | ||
|
4468d81472 | ||
|
7ddae8f2eb | ||
|
6b2851a671 | ||
|
4763969c8f | ||
|
f9b75e486c | ||
|
f868052062 | ||
|
61f0cbe10a | ||
|
0f4968d75c | ||
|
3077c22e4f | ||
|
0decc37b5a | ||
|
4a953b66e0 | ||
|
8435eeed4d | ||
|
cffb4de83b | ||
|
55175087c4 | ||
|
0ddfa8ad10 | ||
|
b74fb3f179 | ||
|
9d3d33b6a2 | ||
|
2abaa60c2e | ||
|
d5547f5c7c | ||
|
061510098c | ||
|
4fc3546887 | ||
|
868948509a | ||
|
e71492a2b3 | ||
|
c2055f3514 | ||
|
2809579dd7 | ||
|
c7afe3e9a4 | ||
|
cd026cd865 | ||
|
bc705aac03 | ||
|
a7c73036f0 | ||
|
e80d27f273 | ||
|
23adbf9540 | ||
|
a7347238e6 | ||
|
bf55666492 | ||
|
8ab0b0e460 | ||
|
b8e1849cec | ||
|
2255d05664 | ||
|
45a8eda49b | ||
|
b1207949ac | ||
|
d61fd01d61 | ||
|
4d5d3108fb | ||
|
e422f4154f | ||
|
0651f1bcd4 | ||
|
11f0ade921 | ||
|
5cab2e1c43 | ||
|
6381bdbf33 | ||
|
c8bcd4af2d | ||
|
cabb06230c | ||
|
4a75566a3b | ||
|
d9c924b472 | ||
|
52aa52c3b1 | ||
|
d254d6075a | ||
|
8004080643 | ||
|
13878974a6 | ||
|
6689e00e6a | ||
|
9b8a108f86 | ||
|
a3569d88c9 | ||
|
f2f6b6ede9 | ||
|
7f81cfd45a | ||
|
13b8e1a2ae | ||
|
36dbdf955d | ||
|
714b6c98ef | ||
|
7181d7ae6a | ||
|
9422d57283 | ||
|
5724cec1e5 | ||
|
35ca54d98f | ||
|
7398a9ebf9 | ||
|
4188fb536e | ||
|
a11e5e2deb | ||
|
5dd7e8c4d9 | ||
|
40bc390c3b | ||
|
8776348a80 | ||
|
49fb4421dd | ||
|
d232f08933 | ||
|
feba4da0b8 | ||
|
928d7186b5 | ||
|
3149a95d6a | ||
|
ed7ebfd58d | ||
|
808e310799 | ||
|
23fbb3cf22 | ||
|
c11e643f13 | ||
|
f5541860bc | ||
|
83ff0055b6 | ||
|
213f23aaf6 | ||
|
9970f3786f | ||
|
d01a568b8b | ||
|
e397884e1e | ||
|
ef65bac79b | ||
|
c0837c726f | ||
|
ce5429aba7 | ||
|
aa191e9202 | ||
|
a8f0283e93 | ||
|
999e47a26c | ||
|
a8103ca22d | ||
|
ef17f4913b | ||
|
1a30043776 | ||
|
b7e8b91eec | ||
|
3be1904653 | ||
|
cadd0e4244 | ||
|
f35ea9a3ca | ||
|
0971630e01 | ||
|
b60a9fa371 | ||
|
ab45360808 | ||
|
8523c0da32 | ||
|
f0dfa4d53d | ||
|
f2bedddce4 | ||
|
552138e851 | ||
|
ff71379a8e | ||
|
23a7feeb6e | ||
|
34f5db9ed4 | ||
|
9fd70cbdf9 | ||
|
87506852d1 | ||
|
e6893bc419 | ||
|
ebeab06710 | ||
|
1911c3690d | ||
|
3f3254a4df | ||
|
20ccb7b558 | ||
|
8cfe89604a | ||
|
15f0ba839f | ||
|
c400b914e5 | ||
|
a431c829cb | ||
|
25d0e125e5 | ||
|
b89a78ce17 | ||
|
f568012b9d | ||
|
328fb70e54 | ||
|
2f24e5ceb7 | ||
|
97809277df | ||
|
3d3e7a330c | ||
|
adf2275018 | ||
|
c1ecfbfe63 | ||
|
a7e7680789 | ||
|
382c19024f | ||
|
9379487942 | ||
|
5e99baf7b9 | ||
|
31951dae4c | ||
|
b1ab7f46e0 | ||
|
4e0c7ed5a6 | ||
|
2ef76d5e31 | ||
|
d3dda443cd | ||
|
ec3802c180 | ||
|
76654c7856 | ||
|
90239bb969 | ||
|
a6e4b0fea5 | ||
|
8ad37af70e | ||
|
c102d359f8 | ||
|
a07891e3e6 | ||
|
e946a35b18 | ||
|
68d7337f98 | ||
|
8b4198fd8d | ||
|
9c795fa40d | ||
|
5f2217a079 | ||
|
0cda4b7f23 | ||
|
e17e69604c | ||
|
e1bf4d4344 | ||
|
0c8a32b9ae | ||
|
d8c60aaae7 | ||
|
3216d65353 | ||
|
0df8d0a4e2 | ||
|
43f19034b9 | ||
|
2e6a937240 | ||
|
05d8a7f6be | ||
|
ceaf4bc6bc | ||
|
f2cc2f433c | ||
|
858aa6255c | ||
|
1d8ea9e869 | ||
|
8ead0b5483 | ||
|
72c081cfd6 | ||
|
1f701c94a7 | ||
|
acfc49fd41 | ||
|
9c29bd627e | ||
|
d24b141d8b | ||
|
bac9e005ae | ||
|
60bcca54eb | ||
|
eccea8a911 | ||
|
0833d0d8d5 | ||
|
55fce489bc | ||
|
2ce40c3c07 | ||
|
255cfb7d62 | ||
|
8a327be400 | ||
|
1cae76b443 | ||
|
828e8d2ae4 | ||
|
ce35509a40 | ||
|
24b8b4e904 | ||
|
c2c1dc7469 | ||
|
47c1071bb8 | ||
|
e401651f09 | ||
|
1449def040 | ||
|
d4d1ec2568 | ||
|
7455ac9bfb | ||
|
b6b1dcb275 | ||
|
2e4f49a223 | ||
|
88d4108f92 | ||
|
c9f3c02cb7 | ||
|
557372b312 | ||
|
18b24535fe | ||
|
20e0bca71d | ||
|
6977f655e3 | ||
|
e6fc9dc75d | ||
|
b57b3f4588 | ||
|
776ae92416 | ||
|
6ba53c1ac0 | ||
|
699d419c71 | ||
|
d4870b3854 | ||
|
e49a9096a5 | ||
|
33148ba832 | ||
|
bca23dd896 | ||
|
3a483a1b20 | ||
|
310d33b8ee | ||
|
8b5dcc3872 | ||
|
36d9af10f0 | ||
|
0c8fcbc684 | ||
|
7edab27e53 | ||
|
9b3531f634 | ||
|
d8f85a8981 | ||
|
3b229489c2 | ||
|
c2fcbbb0c6 | ||
|
8e82bf69d8 | ||
|
284f9e3f2f | ||
|
5ffc3561ed | ||
|
952f1271b4 | ||
|
250b445eee | ||
|
331472b592 | ||
|
82d5afe996 | ||
|
7361d5d3ea | ||
|
6086614274 | ||
|
6bb4780d32 | ||
|
631f5b4848 | ||
|
0f17712f6c | ||
|
4941e0f8ad | ||
|
fbee18e24d | ||
|
f37b2bc7c1 | ||
|
56278c6394 | ||
|
b1d6c03190 | ||
|
1200a77b22 | ||
|
bb9a18ef08 | ||
|
a8b493cb7d | ||
|
1a275ba184 | ||
|
187848660c | ||
|
c3f391dc5a | ||
|
971e6f2664 | ||
|
a1d6ef04ec | ||
|
ff47ab12fb | ||
|
a97e361661 | ||
|
34d84522dd | ||
|
3390fbf238 | ||
|
98ad1feaeb | ||
|
37e8a91d5b | ||
|
68c27eb2a1 | ||
|
143e75d213 | ||
|
295ac49f83 | ||
|
d31a498e6c | ||
|
2fd88a1c6a | ||
|
fbab930cb1 | ||
|
bfcbd72e4b | ||
|
d9d3caec22 | ||
|
238a56cac3 | ||
|
51765b1f65 | ||
|
7159ecb632 | ||
|
11f3425876 | ||
|
e131395cce | ||
|
d26ea2b948 | ||
|
5584a9ce5d | ||
|
e2e8b733a8 | ||
|
f18bf35b20 | ||
|
44d8cb77e3 | ||
|
fc10e3fcac | ||
|
b92ccb2de4 | ||
|
30af6e73be | ||
|
2895b9bfb7 | ||
|
84bd86f1ee | ||
|
7193f40b87 | ||
|
acd69fa4b0 | ||
|
71d8f5f96f | ||
|
856a2096eb | ||
|
76cb129d3c | ||
|
246d5cd587 | ||
|
f390671018 | ||
|
512ec7fb87 | ||
|
24219422df | ||
|
aed20cf7c6 | ||
|
6584933e92 | ||
|
8cf8da4c78 | ||
|
ad1f210528 | ||
|
cadab61a21 | ||
|
0b87f489d5 | ||
|
e3e5e016d5 | ||
|
3425b9a82e | ||
|
5ecfc58e5f | ||
|
6011dd7372 | ||
|
7ed45d26c2 | ||
|
fcdc85eab8 | ||
|
56a2924a33 | ||
|
09886a3f11 | ||
|
e8ec0dc701 | ||
|
41d9d57c84 | ||
|
b87bcc8f5f | ||
|
0541d8f1c5 | ||
|
4606693e62 | ||
|
e61d8e4fb8 | ||
|
6b4685b333 | ||
|
d4ac818a0b | ||
|
c17d95bfed | ||
|
f14184df30 | ||
|
47bbea9ba7 | ||
|
7265935cd4 | ||
|
40bda4b3d1 | ||
|
b7891f92a5 | ||
|
8b79017d0f | ||
|
5465ac9ba0 | ||
|
b8eaa9a527 | ||
|
5d274008db | ||
|
220e755596 | ||
|
fc5034696d | ||
|
4a6f77bc59 | ||
|
1e87850952 | ||
|
3472241289 | ||
|
a944b6ff79 | ||
|
6c4741bc61 | ||
|
8a42ef431b | ||
|
2cf1f0e8fc | ||
|
55e50eced6 | ||
|
30a2923380 | ||
|
f2b3752f3d | ||
|
36945a67d0 | ||
|
c5a2ca5c98 | ||
|
9f2ce0e296 | ||
|
55e10d8287 | ||
|
94c772e1a8 | ||
|
2b6d9c34c9 | ||
|
1a5c86d32e | ||
|
c7219fbdff | ||
|
4c249a1186 | ||
|
158ea1d43b | ||
|
535b8458d4 | ||
|
49b3f82675 | ||
|
9543203610 | ||
|
cacfe4d387 | ||
|
a7f0137e5f | ||
|
b2eb364a4b | ||
|
bb3b1bc6ba | ||
|
ae8eb3f177 | ||
|
27c7325174 | ||
|
8ee3d810b0 | ||
|
d7dde06552 | ||
|
448495a4a3 | ||
|
52cc8e2fcf | ||
|
fb9cdea008 | ||
|
6c54bf036c | ||
|
657bc4edcd | ||
|
b75dd5ff73 | ||
|
607664c860 | ||
|
094bd49f35 | ||
|
18fbe60381 | ||
|
f1956119fb | ||
|
876170767c | ||
|
f4f77ec88b | ||
|
9ba2038702 | ||
|
b176ba9e80 | ||
|
cacb47ca7e | ||
|
500e424fee | ||
|
f1b02c0cab | ||
|
828302702b | ||
|
0827d4fe40 | ||
|
ebff965414 | ||
|
b45954c560 | ||
|
80307a3bcd | ||
|
e8acb5967b | ||
|
939806f021 | ||
|
cc47bf8f6c | ||
|
8b80aaebf5 | ||
|
8f35ebc0c2 | ||
|
793bdd9743 | ||
|
9d50f52bb2 | ||
|
db62afbb64 | ||
|
3dc3e8e40e | ||
|
c79fc81292 | ||
|
62a6a878d4 | ||
|
4efb533d2e | ||
|
891473c7fc | ||
|
81e8ef5c40 | ||
|
e17885088b | ||
|
494340fe67 | ||
|
01625dfd49 | ||
|
a154f1540f | ||
|
5254fc6efe | ||
|
afa2e9c2f7 | ||
|
a4c8f1a0a4 | ||
|
d5a9a9a2d8 | ||
|
3b8f8fb0ef | ||
|
677033afca | ||
|
8e0d76b63b | ||
|
64df41ed90 | ||
|
442a372142 | ||
|
95e5baa967 | ||
|
b80edfb996 | ||
|
e06543bd17 | ||
|
cca8412921 | ||
|
d15a5ad5c1 | ||
|
8ed75dd176 | ||
|
d43ad263ea | ||
|
e602008459 | ||
|
322a18f0e6 | ||
|
05eb749e66 | ||
|
7e8d31a62c | ||
|
8ca0582afc | ||
|
b9f5fca546 | ||
|
5c2dcb5e74 | ||
|
3ef4af9326 | ||
|
f89f7ecd3e | ||
|
be0c457445 | ||
|
3c04dbb4ed | ||
|
cfca367eef | ||
|
8a243c5872 | ||
|
b30b3f3aeb | ||
|
2d3906450a | ||
|
5ecc46d85b | ||
|
ddd6f3dbe5 | ||
|
f5237db580 | ||
|
761fd13a70 | ||
|
e3fa06d7ff | ||
|
61a76f4493 | ||
|
c5f46702c8 | ||
|
c24e5cd3f6 | ||
|
cc3035267f | ||
|
29ee8250e9 | ||
|
47ed0b7627 | ||
|
423280cee1 | ||
|
8c0faa0b9a | ||
|
45ab2a3d7a | ||
|
8804f5c423 | ||
|
1078aa499c | ||
|
05d015169c | ||
|
6c9e7586d5 | ||
|
28bed0041e | ||
|
6f03e30151 | ||
|
6085d6ffd1 | ||
|
6ecd93d0c9 | ||
|
863ea7294f | ||
|
af59824819 | ||
|
80e17ab721 | ||
|
f89d69b081 | ||
|
5ba413569e | ||
|
b3e969f000 | ||
|
a9a40ca46c | ||
|
79aada0b87 | ||
|
162a7b56fe | ||
|
69dc22c10f | ||
|
9642cda949 | ||
|
dd6a9a0b84 | ||
|
945dcc6c2b | ||
|
f60791ac7c | ||
|
b64155a165 | ||
|
472f7725c7 | ||
|
90f01b5fc4 | ||
|
20a2daa114 | ||
|
9b6a2c7068 | ||
|
0a4d3ef6e6 | ||
|
2dc1851892 | ||
|
8d721d086c | ||
|
6f0bb30def | ||
|
c54f04ef4c | ||
|
eb83081a5c | ||
|
8976930e20 | ||
|
164a81776e | ||
|
9c8526db49 | ||
|
d92f9b4dbd | ||
|
1556163fb0 | ||
|
616962200a | ||
|
eb9c44a347 | ||
|
f36ac48de0 | ||
|
f63267a7f9 | ||
|
2c8dcd86e5 | ||
|
9631519eb1 | ||
|
557d076d2e | ||
|
8a40c91d1a | ||
|
90efa3b610 | ||
|
35dc4800e0 | ||
|
ec4e4d3b72 | ||
|
63312ac4b8 | ||
|
d158a7d51e | ||
|
209a2ab3ec | ||
|
009fa2f1b7 | ||
|
5221767a70 | ||
|
9492ff26c6 | ||
|
06764d0f36 | ||
|
0db76aada0 | ||
|
7362416afb | ||
|
9be854031f | ||
|
709851503f | ||
|
6ca47dc3b3 | ||
|
d8774901ee | ||
|
8dba2a88e2 | ||
|
2c33c64fae | ||
|
b9ae01d819 | ||
|
2390b80359 | ||
|
c34c3eb016 | ||
|
85d159bdd1 | ||
|
da1d2b2c48 | ||
|
5f8b0b3c89 | ||
|
0f868f7649 | ||
|
8b0785996e | ||
|
645d8e0ebd | ||
|
3a3c0befa7 | ||
|
80c40e6050 | ||
|
31730e7197 | ||
|
7d76db00ac | ||
|
8aceb8bf53 | ||
|
a17d1be7a4 | ||
|
1e6cc63abe | ||
|
3b300a4d6a | ||
|
3e727c5e5d | ||
|
e0edac32c7 | ||
|
e4b3bc4209 | ||
|
51b09efceb | ||
|
2dabacd024 | ||
|
0613dcc87b | ||
|
d59a2a4cfc | ||
|
2c091f3a3c | ||
|
cfcdc53185 | ||
|
bd33018660 | ||
|
e7589945a2 | ||
|
67a5594909 | ||
|
70ad656af0 | ||
|
f22b83d379 | ||
|
114f84c948 | ||
|
7875df0d6b | ||
|
0d286b99d5 | ||
|
e03d4f2de8 | ||
|
7fddce4a1f | ||
|
3cd8b7d882 | ||
|
3578a3e311 | ||
|
a9ad8d67aa | ||
|
43e1121618 | ||
|
97dd1b4cf3 | ||
|
288635abc9 | ||
|
7884423e05 | ||
|
1615fa63e3 | ||
|
39ab1e1ea7 | ||
|
10c048d84a | ||
|
a458482e51 | ||
|
de46dbd56f | ||
|
60365ad36a | ||
|
466e6d9b30 | ||
|
11d48554e3 | ||
|
b9bcfe36ff | ||
|
05faa7bdf2 | ||
|
88b1f0ca7f | ||
|
f0f7bda2f3 | ||
|
664d0ea023 | ||
|
cd7c00ef8e | ||
|
3f20981aab | ||
|
f54a812ad5 | ||
|
aa1d67a4de | ||
|
feb2150d9b | ||
|
3aa525b0c0 | ||
|
6323d5afed | ||
|
e3e3b4da58 | ||
|
a0bd517380 | ||
|
4316b15ae2 | ||
|
ef1ce7d4d6 | ||
|
a643f40cce | ||
|
47099e2855 | ||
|
2deb2bf03f | ||
|
3bb05edb52 | ||
|
ee2169dd13 | ||
|
11282aaca3 | ||
|
909bc92c01 | ||
|
a6f9272d4b | ||
|
911596daf8 | ||
|
8d8482d60b | ||
|
f3cc8c71c4 | ||
|
f6b3c62b06 | ||
|
26afe04926 | ||
|
500a755250 | ||
|
d02bf258af | ||
|
56990618e9 | ||
|
54a85d3a63 | ||
|
d56beedd7a | ||
|
c2d3e99ddd | ||
|
5e48b3f7f7 | ||
|
00d62b3423 | ||
|
c957124fad | ||
|
28063aa7f7 | ||
|
c458e4a93b | ||
|
e7ae62ba76 | ||
|
ff5411a93a | ||
|
d3073e5e23 | ||
|
34ae4844fa | ||
|
8191ec01e5 | ||
|
4d39ab9753 | ||
|
7a1df15724 | ||
|
42b5574f9b | ||
|
5f4be0de35 | ||
|
6c605f55f9 | ||
|
774c6e8ac0 | ||
|
17be6b860e | ||
|
474ac4a15d | ||
|
b8fedf76cd | ||
|
ca88c59d4e | ||
|
d69e62589a | ||
|
c9d4995900 | ||
|
279e809aaf | ||
|
d61857efae | ||
|
0d037e96fb | ||
|
6376f9aef4 | ||
|
f5a1fa21f7 | ||
|
4a90dac68e | ||
|
2624e56de5 | ||
|
aef54d6694 | ||
|
e839a7784c | ||
|
bea99abd8d | ||
|
a5c4ae955a | ||
|
d7d53849a3 | ||
|
b354f722be | ||
|
ea1aad9774 | ||
|
817d644795 | ||
|
56b8c4bfdb | ||
|
c65c26b463 | ||
|
bf059e5ee8 | ||
|
15a5f425bf | ||
|
3125d24ded | ||
|
6b6d105c2f | ||
|
bab44c52ae | ||
|
2f19605750 | ||
|
7311db3a63 | ||
|
0a00936e99 | ||
|
7cac207c00 | ||
|
feb412b02a | ||
|
199de9ebc0 | ||
|
504ae0193f | ||
|
1ae403b742 | ||
|
939c99cdbf | ||
|
2f36acae49 | ||
|
e938132c53 | ||
|
1b34286264 | ||
|
54f4f6d2d7 | ||
|
07e5e8e67a | ||
|
3c25094495 | ||
|
95e3a5944d | ||
|
64f29120c8 | ||
|
d92434b1e9 | ||
|
01cd056bcc | ||
|
56f2ea3ec2 | ||
|
d45695a088 | ||
|
2ceeac41fe | ||
|
cc4ada99d8 | ||
|
16d67f55b3 | ||
|
18b41adbf6 | ||
|
898fdf7a60 | ||
|
78143c2ff4 | ||
|
7b1524ad01 | ||
|
5799485b0f | ||
|
9cddb6ca39 | ||
|
defc17ba46 | ||
|
106d5afba1 | ||
|
0a9965292c | ||
|
4fb66e632f | ||
|
5787895794 | ||
|
d90489b31d | ||
|
f38912a0c9 | ||
|
f24ab8508e | ||
|
2d8fff099f | ||
|
dce36d8ded | ||
|
18d0278279 | ||
|
4e525040f3 | ||
|
fdf2649f2f | ||
|
b09c660833 | ||
|
c02a24cf71 | ||
|
a77136bd1d | ||
|
e0eb5eb2b1 | ||
|
714fd93292 | ||
|
a293b5a371 | ||
|
1c93868ae1 | ||
|
61ff0452e1 | ||
|
406098e55a | ||
|
a2825be819 | ||
|
6830b08723 | ||
|
eead2f059b | ||
|
291c111ce8 | ||
|
2ccf063dfe | ||
|
49aee8b931 | ||
|
f2a3b557c8 | ||
|
b0eceddcec | ||
|
0a062d26e6 | ||
|
20b2dd6b19 | ||
|
83592a5e70 | ||
|
70fb733fea | ||
|
eb8333c772 | ||
|
c9a98b68c8 | ||
|
247d3ed729 | ||
|
031c6428d5 | ||
|
f2e9d585f7 | ||
|
8af9853b9a | ||
|
783f50657b | ||
|
3a50b91722 | ||
|
3631d1349e | ||
|
05c8687041 | ||
|
66f32b7601 | ||
|
9101916719 | ||
|
edb594461d | ||
|
9464b71a6e | ||
|
7d56e2a937 | ||
|
218aa03f05 | ||
|
1db03a10d7 | ||
|
e1f818ffb7 | ||
|
9934f505a5 | ||
|
8c2b8f7241 | ||
|
bf70c2c660 | ||
|
74b912a0b7 | ||
|
93d40b083e | ||
|
162ee28d0c | ||
|
d328d17d03 | ||
|
2778d88e8a | ||
|
b5c6178644 | ||
|
ea8927e1da | ||
|
bc1065a7fc | ||
|
f4c7d389e5 | ||
|
03387391de | ||
|
24df5f5208 | ||
|
9348a8ab15 | ||
|
fbd55dd740 | ||
|
e5e26413e9 | ||
|
527fe2f5e1 | ||
|
38d213ee6c | ||
|
613f2d3b86 | ||
|
e7ad972783 | ||
|
2f939d8c56 | ||
|
7ef751f96d | ||
|
fb66fb12c5 | ||
|
042dcf795c | ||
|
aad39c5ffc | ||
|
c3ed710e31 | ||
|
cb65907e60 | ||
|
bb8a263d70 | ||
|
70fd94edb3 | ||
|
d0492898eb | ||
|
c6ba03802f | ||
|
1bebcef265 | ||
|
209fbf82c4 | ||
|
6ce79ae1d0 | ||
|
b3eb5c4f0e | ||
|
ec4e9780ed | ||
|
22abbffbb7 | ||
|
05a724afae | ||
|
1957b002bc | ||
|
04cef25add | ||
|
ab1a6d8829 | ||
|
98afb0e998 | ||
|
e9d81fc883 | ||
|
22fac5e1e0 | ||
|
b6a2a4ad5a | ||
|
5f00347019 | ||
|
89a22ded54 | ||
|
1e31fa89aa | ||
|
b477b7f777 | ||
|
0de15f7a97 | ||
|
35ecf40259 | ||
|
009363a7bb | ||
|
a3758612ec | ||
|
8dc94e014f | ||
|
ad886ac164 | ||
|
78e7c8b8e9 | ||
|
3779085051 | ||
|
c29c4ceb0f | ||
|
07fc047dd8 | ||
|
bc46694ea7 | ||
|
0c2827e130 | ||
|
cb09e0bc9a | ||
|
06d5ea9d51 | ||
|
a4650c6226 | ||
|
c85a603491 | ||
|
536585b846 | ||
|
5b472ff67c | ||
|
6f2ec22894 | ||
|
05798fe07a | ||
|
8ab5ffd876 | ||
|
2b6709d83f | ||
|
e4cc5b3847 | ||
|
8bad56e897 | ||
|
92e691408f | ||
|
0a9c234127 | ||
|
0283a1ab74 | ||
|
b32096b16e | ||
|
3d7582faec | ||
|
54a88ab5ab | ||
|
aff5ff08d5 | ||
|
fc0440546f | ||
|
172b8d2427 | ||
|
6d10a498a5 | ||
|
881819ed5f | ||
|
3275c5f710 | ||
|
b0cc1a38c3 | ||
|
e8025dbc81 | ||
|
e10182c839 | ||
|
b4f1fe08f0 | ||
|
64171fa2a1 | ||
|
048eacd305 | ||
|
24aa72c19d | ||
|
92b5fe4be4 | ||
|
939055f19c | ||
|
c47f3e3307 | ||
|
cf6a1ac9ad | ||
|
f1146a3443 | ||
|
6823a62644 | ||
|
2516a1e298 | ||
|
288f93c5dd | ||
|
8ef64dbe74 | ||
|
01e091fd17 | ||
|
8b4c0b456b | ||
|
02a601deff | ||
|
b207fadc04 | ||
|
bf3883ed46 | ||
|
3a58e9d33a | ||
|
570c54002f | ||
|
d51c067e1b | ||
|
2fea5d428d | ||
|
2b8a8b03a8 | ||
|
d51e0c49b1 | ||
|
d913534793 | ||
|
36907edd50 | ||
|
7ec169ab10 | ||
|
7873da1ae5 | ||
|
ef5a6e7880 | ||
|
96f01e670f | ||
|
10139241f5 | ||
|
5902be2a49 | ||
|
cc946ce068 | ||
|
1102fdc44b | ||
|
c995c81fff | ||
|
6684af9938 | ||
|
8bafd12f95 | ||
|
76266cf31b | ||
|
4ad0cdf5d4 | ||
|
1a87d3a659 | ||
|
085f2c6ca0 | ||
|
98fdb95645 | ||
|
3035c9a366 | ||
|
5004cf331a | ||
|
74eb8c8622 | ||
|
b74300f67c | ||
|
107babe8f4 | ||
|
8fd9a22d18 | ||
|
8c5c1316dd | ||
|
daff5d8b5a | ||
|
6c4a7b626e | ||
|
1aa5943e67 | ||
|
69fe889f92 | ||
|
2ef87ad110 | ||
|
c655e6ea73 | ||
|
5d7ef9281f | ||
|
6fa00e7cc8 | ||
|
2f2825f15e | ||
|
e377fe5503 | ||
|
69b41dd72e | ||
|
69fa9874dd | ||
|
a620a5c430 | ||
|
1f3db8b602 | ||
|
f6732a484e | ||
|
4015122d33 | ||
|
42eb811910 | ||
|
c9042ffedd | ||
|
4e2c6a7b8e | ||
|
7453bf2ee6 | ||
|
7d17c652f3 | ||
|
075f00cecd | ||
|
b9f2ba0717 | ||
|
ed04be5faa | ||
|
90119c75d5 | ||
|
d30056026b | ||
|
bc8a5916d8 | ||
|
f202602f99 | ||
|
69f361a3a1 | ||
|
da9ff0cc66 | ||
|
703ff09a91 | ||
|
30035ce1c2 | ||
|
39b74ebfd4 | ||
|
3bd677c102 | ||
|
4eb7d2868c | ||
|
0e4473685b | ||
|
b721ed49ab | ||
|
25601b9fcc | ||
|
db10c8ab46 | ||
|
55f78e3b64 | ||
|
7abf349730 | ||
|
f9d41caeb6 | ||
|
b166410cbf | ||
|
31d5a7ae9e | ||
|
5bb91759b4 | ||
|
80c4b7c9bb | ||
|
ea0a9ceb37 | ||
|
68dd2a6b91 | ||
|
f875976268 | ||
|
e23c6899e0 | ||
|
c35c7b2cea | ||
|
1c2aa44d46 | ||
|
3b44a0da32 | ||
|
58353e2839 | ||
|
5e0572637e | ||
|
501b26decd | ||
|
c6596f2c54 | ||
|
2863308090 | ||
|
7fef1fdc83 | ||
|
3081c151bd | ||
|
c793295be0 | ||
|
fe0a35cc7a | ||
|
0b67eebaec | ||
|
03201e2f20 | ||
|
dfc32b26a6 | ||
|
173ad339bb | ||
|
89fd962615 | ||
|
a928cd3fa1 | ||
|
f190a6ebc5 | ||
|
9beb9fd941 | ||
|
525a1228c3 | ||
|
19fd25c7cd | ||
|
5b0927ca4b | ||
|
324d4433c3 | ||
|
615ca56ea3 | ||
|
41dd163453 | ||
|
5cd2c77d98 | ||
|
f270f7430c | ||
|
99b23627d0 | ||
|
4d731ca30b | ||
|
f2016f26d7 | ||
|
a8818c16d8 | ||
|
7bcea98d0c | ||
|
74b15d10d9 | ||
|
356c70cdae | ||
|
dfb5c37d98 | ||
|
a0603ad3b7 | ||
|
551add5f44 | ||
|
88944a31ec | ||
|
db1e5f10ea | ||
|
2e398c5da4 | ||
|
ff6df76e36 | ||
|
bb7a7d94ef | ||
|
9a475cc010 | ||
|
ae30c285a2 |
@@ -9,7 +9,7 @@ end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = tab
|
||||
tab_width = 2
|
||||
tab_width = 4
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
|
36
.github/ISSUE_TEMPLATE.md
vendored
36
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,33 +1,37 @@
|
||||
**In raising this issue, I confirm the following (please check boxes, eg [X]) Failure to fill the template will close your issue:**
|
||||
**In raising this issue, I confirm the following:** `{please fill the checkboxes, e.g: [X]}`
|
||||
|
||||
- [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md).
|
||||
- [] The issue I am reporting can be *replicated*
|
||||
- [] The issue I am reporting can be *replicated*.
|
||||
- [] The issue I am reporting isn't a duplicate (see [FAQs](https://github.com/pi-hole/pi-hole/wiki/FAQs), [closed issues](https://github.com/pi-hole/pi-hole/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), and [open issues](https://github.com/pi-hole/pi-hole/issues)).
|
||||
|
||||
**How familiar are you with the codebase?:**
|
||||
**How familiar are you with the the source code relevant to this issue?:**
|
||||
|
||||
_{replace this text with a number from 1 to 10, with 1 being not familiar, and 10 being very familiar}_
|
||||
`{Replace this with a number from 1 to 10. 1 being not familiar, and 10 being very familiar}`
|
||||
|
||||
---
|
||||
**[BUG REPORT | OTHER]:**
|
||||
**Expected behaviour:**
|
||||
|
||||
Please [submit your feature request here](https://discourse.pi-hole.net/c/feature-requests), so it is votable by the community. It's also easier for us to track.
|
||||
`{A detailed description of what you expect to see}`
|
||||
|
||||
**[BUG | ISSUE] Expected Behaviour:**
|
||||
**Actual behaviour:**
|
||||
|
||||
`{A detailed description and/or screenshots of what you do see}`
|
||||
|
||||
**[BUG | ISSUE] Actual Behaviour:**
|
||||
**Steps to reproduce:**
|
||||
|
||||
`{Detailed steps of how we can reproduce this}`
|
||||
|
||||
**[BUG | ISSUE] Steps to reproduce:**
|
||||
**Debug token provided by [uploading `pihole -d` log](https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738#debug):**
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
`{Alphanumeric token}`
|
||||
|
||||
**(Optional) Debug token generated by `pihole -d`:**
|
||||
**Troubleshooting undertaken, and/or other relevant information:**
|
||||
|
||||
`<token>`
|
||||
`{Steps of what you have done to fix this}`
|
||||
|
||||
_This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._
|
||||
> * `{Please delete this quoted section when opening your issue}`
|
||||
> * You must follow the template instructions. Failure to do so will result in your issue being closed.
|
||||
> * Please [submit any feature requests here](https://discourse.pi-hole.net/c/feature-requests), so it is votable and trackable by the community.
|
||||
> * Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time.
|
||||
> * Detail helps us understand and resolve an issue quicker, but please ensure it's relevant.
|
||||
> * _This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._
|
||||
|
38
.github/PULL_REQUEST_TEMPLATE.md
vendored
38
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,19 +1,31 @@
|
||||
**By submitting this pull request, I confirm the following (please check boxes, eg [X]) _Failure to fill the template will close your PR_:**
|
||||
**By submitting this pull request, I confirm the following:**
|
||||
*please fill any appropriate checkboxes, e.g: [X]*
|
||||
|
||||
***Please submit all pull requests against the `development` branch. Failure to do so will delay or deny your request***
|
||||
- [ ] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md), as well as this entire template.
|
||||
- [ ] I have made only one major change in my proposed changes.
|
||||
- [ ] I have commented my proposed changes within the code.
|
||||
- [ ] I have tested my proposed changes, and have included unit tests where possible.
|
||||
- [ ] I am willing to help maintain this change if there are issues with it later.
|
||||
- [ ] I give this submission freely and claim no ownership.
|
||||
- [ ] It is compatible with the [EUPL 1.2 license](https://opensource.org/licenses/EUPL-1.1)
|
||||
- [ ] I have squashed any insignificant commits. ([`git rebase`](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html))
|
||||
|
||||
- [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md).
|
||||
- [] I have checked that [another pull request](https://github.com/pi-hole/pi-hole/pulls) for this purpose does not exist.
|
||||
- [] I have considered, and confirmed that this submission will be valuable to others.
|
||||
- [] I accept that this submission may not be used, and the pull request closed at the will of the maintainer.
|
||||
- [] I give this submission freely, and claim no ownership to its content.
|
||||
|
||||
**How familiar are you with the codebase?:**
|
||||
|
||||
_{replace this text with a number from 1 to 10, with 1 being not familiar, and 10 being very familiar}_
|
||||
Please make sure you [Sign Off](https://github.com/pi-hole/pi-hole/wiki/How-to-signoff-your-commits.) all commits. Pi-hole enforces the [DCO](https://github.com/pi-hole/pi-hole/wiki/Contributing-to-the-project).
|
||||
|
||||
---
|
||||
_{replace this line with your pull request content}_
|
||||
**What does this PR aim to accomplish?:**
|
||||
*A detailed description, screenshots (if necessary), as well as links to any relevant GitHub issues*
|
||||
|
||||
|
||||
_This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._
|
||||
**How does this PR accomplish the above?:**
|
||||
*A detailed description (such as a changelog) and screenshots (if necessary) of the implemented fix*
|
||||
|
||||
|
||||
**What documentation changes (if any) are needed to support this PR?:**
|
||||
*A detailed list of any necessary changes*
|
||||
|
||||
|
||||
---
|
||||
* You must follow the template instructions. Failure to do so will result in your pull request being closed.
|
||||
* Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time.
|
||||
|
||||
|
2
.github/dco.yml
vendored
Normal file
2
.github/dco.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
require:
|
||||
members: false
|
72
.gitignore
vendored
72
.gitignore
vendored
@@ -3,4 +3,74 @@
|
||||
*.swp
|
||||
__pycache__
|
||||
.cache
|
||||
.pullapprove.yml
|
||||
.pytest_cache
|
||||
.tox
|
||||
.eggs
|
||||
*.egg-info
|
||||
|
||||
|
||||
# Created by https://www.gitignore.io/api/jetbrains+iml
|
||||
|
||||
### JetBrains+iml ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# All idea files, with execptions
|
||||
.idea
|
||||
!.idea/codeStyles/*
|
||||
!.idea/codeStyleSettings.xml
|
||||
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Ruby plugin and RubyMine
|
||||
/.rakeTasks
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
### JetBrains+iml Patch ###
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
# End of https://www.gitignore.io/api/jetbrains+iml
|
||||
|
7
.idea/codeStyles/Project.xml
generated
Normal file
7
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<MarkdownNavigatorCodeStyleSettings>
|
||||
<option name="RIGHT_MARGIN" value="72" />
|
||||
</MarkdownNavigatorCodeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
@@ -1,38 +0,0 @@
|
||||
version: 2
|
||||
|
||||
always_pending:
|
||||
title_regex: '(WIP|wip)'
|
||||
labels:
|
||||
- wip
|
||||
explanation: 'This PR is a work in progress...'
|
||||
|
||||
group_defaults:
|
||||
reset_on_push:
|
||||
enabled: true
|
||||
reject_value: -2
|
||||
approve_regex: '^(Approved|:shipit:|:\+1:|Engage|:taco:)'
|
||||
reject_regex: '^(Rejected|:-1:|Borg)'
|
||||
author_approval:
|
||||
auto: true
|
||||
|
||||
|
||||
groups:
|
||||
development:
|
||||
approve_by_comment:
|
||||
enabled: true
|
||||
conditions:
|
||||
branches:
|
||||
- development
|
||||
required: 2
|
||||
teams:
|
||||
- approvers
|
||||
|
||||
master:
|
||||
approve_by_comment:
|
||||
enabled: true
|
||||
conditions:
|
||||
branches:
|
||||
- master
|
||||
required: 4
|
||||
teams:
|
||||
- approvers
|
6
.stickler.yml
Normal file
6
.stickler.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
linters:
|
||||
shellcheck:
|
||||
shell: bash
|
||||
phpcs:
|
||||
csslint:
|
||||
flake8:
|
@@ -7,4 +7,6 @@ python:
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
|
||||
script: py.test -vv
|
||||
script:
|
||||
# tox.ini handles setup, ordering of docker build first, and then run tests
|
||||
- tox
|
||||
|
@@ -1,5 +1,3 @@
|
||||
_This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._
|
||||
|
||||
# Contributors Guide
|
||||
|
||||
Please read and understand the contribution guide before creating an issue or pull request.
|
||||
@@ -28,6 +26,7 @@ When requesting or submitting new features, first consider whether it might be u
|
||||
|
||||
- Check the codebase to ensure that your feature doesn't already exist.
|
||||
- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
|
||||
- Read and understand the [DCO guidelines](https://github.com/pi-hole/pi-hole/wiki/Contributing-to-the-project) for the project.
|
||||
|
||||
## Technical Requirements
|
||||
|
||||
|
290
README.md
290
README.md
@@ -1,176 +1,216 @@
|
||||
<p align="center">
|
||||
<a href=https://www.bountysource.com/trackers/3011939-pi-hole-pi-hole?utm_source=3011939&utm_medium=shield&utm_campaign=TRACKER_BADGE><img src="https://www.bountysource.com/badge/tracker?tracker_id=3011939"></a>
|
||||
<a href="https://www.codacy.com/app/Pi-hole/pi-hole?utm_source=github.com&utm_medium=referral&utm_content=pi-hole/pi-hole&utm_campaign=Badge_Grade"><img src="https://api.codacy.com/project/badge/Grade/c558a0f8d7124c99b02b84f0f5564238"/></a>
|
||||
<a href=https://travis-ci.org/pi-hole/pi-hole><img src="https://travis-ci.org/pi-hole/pi-hole.svg?branch=development"></a>
|
||||
<a href="https://pi-hole.net"><img src="https://pi-hole.github.io/graphics/Vortex/Vortex_with_text.png" width="150" height="255" alt="Pi-hole"></a><br/>
|
||||
<b>Network-wide ad blocking via your own Linux hardware</b><br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href=https://discourse.pi-hole.net><img src="https://assets.pi-hole.net/static/Vortex_with_text_and_TM.png" width=210></a>
|
||||
</p>
|
||||
The Pi-hole[®](https://pi-hole.net/trademark-rules-and-brand-guidelines/) is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) that protects your devices from unwanted content, without installing any client-side software.
|
||||
|
||||
## The multi-platform, network-wide ad blocker
|
||||
- **Easy-to-install**: our versatile installer walks you through the process, and [takes less than ten minutes](https://www.youtube.com/watch?v=vKWjx1AQYgs)
|
||||
- **Resolute**: content is blocked in _non-browser locations_, such as ad-laden mobile apps and smart TVs
|
||||
- **Responsive**: seamlessly speeds up the feel of everyday browsing by caching DNS queries
|
||||
- **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://discourse.pi-hole.net/t/hardware-software-requirements/273)
|
||||
- **Robust**: a command line interface that is quality assured for interoperability
|
||||
- **Insightful**: a beautiful responsive Web Interface dashboard to view and control your Pi-hole
|
||||
- **Versatile**: can optionally function as a [DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026), ensuring *all* your devices are protected automatically
|
||||
- **Scalable**: [capable of handling hundreds of millions of queries](https://pi-hole.net/2017/05/24/how-much-traffic-can-pi-hole-handle/) when installed on server-grade hardware
|
||||
- **Modern**: blocks ads over both IPv4 and IPv6
|
||||
- **Free**: open source software which helps ensure _you_ are the sole person in control of your privacy
|
||||
|
||||
Block ads for **all** your devices _without_ the need to install client-side software. The Pi-hole™ blocks ads at the DNS-level, so all your devices are protected.
|
||||
-----
|
||||
<a href="https://www.codacy.com/app/Pi-hole/pi-hole?utm_source=github.com&utm_medium=referral&utm_content=pi-hole/pi-hole&utm_campaign=Badge_Grade"><img src="https://api.codacy.com/project/badge/Grade/c558a0f8d7124c99b02b84f0f5564238" alt="Codacy Grade"/></a>
|
||||
<a href="https://travis-ci.org/pi-hole/pi-hole"><img src="https://travis-ci.org/pi-hole/pi-hole.svg?branch=development" alt="Travis Build Status"/></a>
|
||||
<a href="https://www.bountysource.com/trackers/3011939-pi-hole-pi-hole?utm_source=3011939&utm_medium=shield&utm_campaign=TRACKER_BADGE"><img src="https://www.bountysource.com/badge/tracker?tracker_id=3011939" alt="BountySource"/></a>
|
||||
|
||||
- Web Browsers
|
||||
- Cell Phones
|
||||
- Smart TV's
|
||||
- Internet-connected home automation
|
||||
- Anything that communicates with the Internet
|
||||
## One-Step Automated Install
|
||||
Those who want to get started quickly and conveniently may install Pi-hole using the following command:
|
||||
|
||||
<p align="center">
|
||||
<a href=http://www.digitalocean.com/?refcode=344d234950e1><img src="https://assets.pi-hole.net/static/DOHostingSlug.png"></a>
|
||||
</p>
|
||||
#### `curl -sSL https://install.pi-hole.net | bash`
|
||||
|
||||
## Your Support Still Matters
|
||||
|
||||
Digital Ocean helps with our infrastructure, but our developers are all volunteers so *your donations help keep us innovating*. Sending a donation using our links below helps us offset a portion of our monthly costs.
|
||||
|
||||
-  [Donate via PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY)
|
||||
-  Bitcoin Address: 1GKnevUnVaQM2pQieMyeHkpr8DXfkpfAtL
|
||||
|
||||
### One-Step Automated Install
|
||||
1. Install a [supported operating system](https://discourse.pi-hole.net/t/hardware-software-requirements/273/1)
|
||||
2. Run the command below (it downloads [this script](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) in case you want to read over it first!)
|
||||
|
||||
### `curl -sSL https://install.pi-hole.net | bash`
|
||||
|
||||
#### Alternative Semi-Automated Install Methods
|
||||
_If you wish to read over the script before running it, run `nano basic-install.sh` to open the file in a text viewer._
|
||||
|
||||
##### Clone our repository and run the automated installer from your device.
|
||||
## Alternative Install Methods
|
||||
[Piping to `bash` is controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash), as it prevents you from [reading code that is about to run](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) on your system. Therefore, we provide these alternative installation methods which allow code review before installation:
|
||||
|
||||
### Method 1: Clone our repository and run
|
||||
```
|
||||
git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole
|
||||
cd Pi-hole/automated\ install/
|
||||
bash basic-install.sh
|
||||
cd "Pi-hole/automated install/"
|
||||
sudo bash basic-install.sh
|
||||
```
|
||||
|
||||
##### Or
|
||||
|
||||
```bash
|
||||
### Method 2: Manually download the installer and run
|
||||
```
|
||||
wget -O basic-install.sh https://install.pi-hole.net
|
||||
bash basic-install.sh
|
||||
sudo bash basic-install.sh
|
||||
```
|
||||
|
||||
Once installed, [configure your router to have **DHCP clients use the Pi as their DNS server**](https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245) and then any device that connects to your network will have ads blocked without any further configuration. Alternatively, you can manually set each device to use Pi-hole™ as their DNS server.
|
||||
## Post-install: Make your network take advantage of Pi-hole
|
||||
|
||||
## What is Pi-hole™ and how do I install it?
|
||||
<p align="center">
|
||||
<a href=https://www.youtube.com/watch?v=vKWjx1AQYgs><img src="https://assets.pi-hole.net/static/video-explainer.png"></a>
|
||||
</p>
|
||||
Once the installer has been run, you will need to [configure your router to have **DHCP clients use Pi-hole as their DNS server**](https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245) which ensures that all devices connecting to your network will have content blocked without any further intervention.
|
||||
|
||||
If your router does not support setting the DNS server, you can [use Pi-hole's built-in DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026); just be sure to disable DHCP on your router first (if it has that feature available).
|
||||
|
||||
## Get Help Or Connect With Us On The Web
|
||||
As a last resort, you can always manually set each device to use Pi-hole as their DNS server.
|
||||
|
||||
- [Users Forum](https://discourse.pi-hole.net/)
|
||||
- [FAQs](https://discourse.pi-hole.net/c/faqs)
|
||||
- [Wiki](https://github.com/pi-hole/pi-hole/wiki)
|
||||
-  [Tweet @The_Pi_Hole](https://twitter.com/The_Pi_Hole)
|
||||
-  [Reddit /r/pihole](https://www.reddit.com/r/pihole/)
|
||||
-  [Pi-hole channel](https://www.youtube.com/channel/UCT5kq9w0wSjogzJb81C9U0w)
|
||||
- [](https://gitter.im/pi-hole/pi-hole?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
-----
|
||||
|
||||
## Technical Details
|
||||
## Pi-hole is free, but powered by your support
|
||||
There are many reoccurring costs involved with maintaining free, open source, and privacy-respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software, as well as the importance of keeping it maintained.
|
||||
|
||||
The Pi-hole™ is an **advertising-aware DNS/Web server**. If an ad domain is queried, a small Web page or GIF is delivered in place of the advertisement.
|
||||
Make no mistake: **your support is absolutely vital to help keep us innovating!**
|
||||
|
||||
### Gravity
|
||||
### Donations
|
||||
Sending a donation using our links below is **extremely helpful** in offsetting a portion of our monthly expenses:
|
||||
|
||||
The [gravity.sh](https://github.com/pi-hole/pi-hole/blob/master/gravity.sh) does most of the magic. The script pulls in ad domains from many sources and compiles them into a single list of [over 1.6 million entries](http://jacobsalmela.com/block-millions-ads-network-wide-with-a-raspberry-pi-hole-2-0) (if you decide to use the [mahakala list](https://github.com/pi-hole/pi-hole/commit/963eacfe0537a7abddf30441c754c67ca1e40965)). This script is controlled by the `pihole` command. Please run `pihole -h` to see what commands can be run via `pihole`.
|
||||
- <img src="https://pi-hole.github.io/graphics/Badges/paypal-badge-black.svg" width="24" height="24" alt="PP"/> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY">Donate via PayPal</a><br/>
|
||||
- <img src="https://pi-hole.github.io/graphics/Badges/bitcoin-badge-black.svg" width="24" height="24" alt="BTC"/> [Bitcoin](https://commerce.coinbase.com/checkout/fb7facaf-bebd-46be-bb77-b358f4546763): <code>
|
||||
3MDPzjXu2hjw5sGLJvKUi1uXbvQPzVrbpF</code></br>
|
||||
- <img src="https://pi-hole.github.io/graphics/Badges/bitcoin-badge-black.svg" width="24" height="24" alt="BTC"/> [Bitcoin Cash](https://commerce.coinbase.com/checkout/fb7facaf-bebd-46be-bb77-b358f4546763): <code>qzqsz4aju2eecc6uhs7tus4vlwhhela24sdruf4qp5</code></br>
|
||||
- <img src="https://pi-hole.github.io/graphics/Badges/ethereum-badge-black.svg" width="24" height="24" alt="BTC"/> [Ethereum](https://commerce.coinbase.com/checkout/fb7facaf-bebd-46be-bb77-b358f4546763): <code>0x79d4e90A4a0C732819526c93e21A3F1356A2FAe1</code>
|
||||
|
||||
### Alternative support
|
||||
If you'd rather not [donate](https://pi-hole.net/donate/) (_which is okay!_), there are other ways you can help support us:
|
||||
- [Patreon](https://patreon.com/pihole) _Become a patron for rewards_
|
||||
- [Digital Ocean](http://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_
|
||||
- [Stickermule](https://www.stickermule.com/unlock?ref_id=6055890701&utm_medium=link&utm_source=invite) _earn a $10 credit after your first purchase_
|
||||
- [Pi-hole Swag Store](https://pi-hole.net/shop/) _affiliate link_
|
||||
- [Amazon](http://www.amazon.com/exec/obidos/redirect-home/pihole09-20) _affiliate link_
|
||||
- [DNS Made Easy](https://cp.dnsmadeeasy.com/u/133706) _affiliate link_
|
||||
- [Vultr](http://www.vultr.com/?ref=7190426) _affiliate link_
|
||||
- Spreading the word about our software, and how you have benefited from it
|
||||
|
||||
### Contributing via GitHub
|
||||
We welcome _everyone_ to contribute to issue reports, suggest new features, and create pull requests.
|
||||
|
||||
#### Other Operating Systems
|
||||
If you have something to add - anything from a typo through to a whole new feature, we're happy to check it out! Just make sure to fill out our template when submitting your request; the questions that it asks will help the volunteers quickly understand what you're aiming to achieve.
|
||||
|
||||
The automated install is only for a clean install of a Debian family or Fedora based system, such as the Raspberry Pi. However, this script will work for most UNIX-like systems, some with some slight **modifications** that we can help you work through. If you can install `dnsmasq` and a web server, it should work OK. If there are other platforms you'd like supported, let us know.
|
||||
You'll find that the [install script](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) and the [debug script](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/piholeDebug.sh) have an abundance of comments, which will help you better understand how Pi-hole works. They're also a valuable resource to those who want to learn how to write scripts or code a program! We encourage anyone who likes to tinker to read through it and submit a pull request for us to review.
|
||||
|
||||
### Web Interface
|
||||
### Presentations about Pi-hole
|
||||
Word-of-mouth continues to help our project grow immensely, and so we are helping make this easier for people.
|
||||
|
||||
The [Web interface](https://github.com/pi-hole/AdminLTE#pi-hole-admin-dashboard) will be installed automatically so you can view stats and change settings. You can find it at:
|
||||
If you are going to be presenting Pi-hole at a conference, meetup or even a school project, [get in touch with us](https://pi-hole.net/2017/05/17/giving-a-presentation-on-pi-hole-contact-us-first-for-some-goodies-and-support/) so we can hook you up with free swag to hand out to your audience!
|
||||
|
||||
`http://192.168.1.x/admin/index.php` or `http://pi.hole/admin`
|
||||
-----
|
||||
|
||||

|
||||
## Getting in touch with us
|
||||
While we are primarily reachable on our <a href="https://discourse.pi-hole.net/">Discourse User Forum</a>, we can also be found on a variety of social media outlets. **Please be sure to check the FAQ's** before starting a new discussion, as we do not have the spare time to reply to every request for assistance.
|
||||
|
||||
### Whitelist and blacklist
|
||||
<ul>
|
||||
<li><a href="https://discourse.pi-hole.net/c/faqs">Frequently Asked Questions</a></li>
|
||||
<li><a href="https://github.com/pi-hole/pi-hole/wiki">Pi-hole Wiki</a></li>
|
||||
<li><a href="https://discourse.pi-hole.net/c/feature-requests?order=votes">Feature Requests</a></li>
|
||||
<li><a href="https://discourse.pi-hole.net/">Discourse User Forum</a></li>
|
||||
<li><a href="https://www.reddit.com/r/pihole/">Reddit</a></li>
|
||||
<li><a href="https://gitter.im/pi-hole/pi-hole">Gitter</a> (Real-time chat)</li>
|
||||
<li><a href="https://twitter.com/The_Pi_Hole">Twitter</a></li>
|
||||
<li><a href="https://www.youtube.com/channel/UCT5kq9w0wSjogzJb81C9U0w">YouTube</a></li>
|
||||
<li><a href="https://www.facebook.com/ThePiHole/">Facebook</a></li>
|
||||
</ul>
|
||||
|
||||
Domains can be whitelisted and blacklisted using either the web interface or the command line. See [the wiki page](https://github.com/pi-hole/pi-hole/wiki/Whitelisting-and-Blacklisting) for more details
|
||||
<p align="center">
|
||||
<a href=https://github.com/pi-hole/pi-hole/wiki/Whitelisting-and-Blacklisting><img src="https://assets.pi-hole.net/static/whitelist212.png"></a>
|
||||
</p>
|
||||
-----
|
||||
|
||||
### Settings
|
||||
## Breakdown of Features
|
||||
### The Command Line Interface
|
||||
The `pihole` command has all the functionality necessary to be able to fully administer the Pi-hole, without the need of the Web Interface. It's fast, user-friendly, and auditable by anyone with an understanding of `bash`.
|
||||
|
||||
The settings page lets you control and configure your Pi-hole™. You can do things like:
|
||||
<a href="https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif"><img src="https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif" alt="Pi-hole Blacklist Demo"/></a>
|
||||
|
||||
- enable Pi-hole's built-in DHCP server
|
||||
- exclude domains from the graphs
|
||||
- configure upstream DNS servers
|
||||
- and more!
|
||||
Some notable features include:
|
||||
* [Whitelisting, Blacklisting and Wildcards](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#whitelisting-blacklisting-and-wildcards)
|
||||
* [Debugging utility](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#debugger)
|
||||
* [Viewing the live log file](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#tail)
|
||||
* [Real-time Statistics via `ssh`](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#chronometer) or [your TFT LCD screen](http://www.amazon.com/exec/obidos/ASIN/B00ID39LM4/pihole09-20)
|
||||
* [Updating Ad Lists](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#gravity)
|
||||
* [Querying Ad Lists for blocked domains](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#query)
|
||||
* [Enabling and Disabling Pi-hole](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#enable--disable)
|
||||
* ... and *many* more!
|
||||
|
||||

|
||||
You can read our [Core Feature Breakdown](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown), as well as read up on [example usage](https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738) for more information.
|
||||
|
||||
#### Built-in DHCP Server
|
||||
### The Web Interface Dashboard
|
||||
This [optional dashboard](https://github.com/pi-hole/AdminLTE) allows you to view stats, change settings, and configure your Pi-hole. It's the power of the Command Line Interface, with none of the learning curve!
|
||||
|
||||
Pi-hole™ ships with a built-in DHCP server. This allows you to let your network devices use Pi-hole™ as their DNS server if your router does not let you adjust the DHCP options.
|
||||
<p align="center">
|
||||
<a href=hhttps://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245><img src="https://assets.pi-hole.net/static/piholedhcpserver.png"></a>
|
||||
</p>
|
||||
<img src="https://pi-hole.github.io/graphics/Screenshots/pihole-dashboard.png" alt="Pi-hole Dashboard"/></a>
|
||||
|
||||
## API
|
||||
Some notable features include:
|
||||
* Mobile friendly interface
|
||||
* Password protection
|
||||
* Detailed graphs and doughnut charts
|
||||
* Top lists of domains and clients
|
||||
* A filterable and sortable query log
|
||||
* Long Term Statistics to view data over user-defined time ranges
|
||||
* The ability to easily manage and configure Pi-hole features
|
||||
* ... and all the main features of the Command Line Interface!
|
||||
|
||||
A basic read-only API can be accessed at `/admin/api.php`. It returns the following JSON:
|
||||
There are several ways to [access the dashboard](https://discourse.pi-hole.net/t/how-do-i-access-pi-holes-dashboard-admin-interface/3168):
|
||||
|
||||
``` json
|
||||
{
|
||||
"domains_being_blocked": "136708",
|
||||
"dns_queries_today": "18108",
|
||||
"ads_blocked_today": "14648",
|
||||
"ads_percentage_today": "80.89"
|
||||
}
|
||||
```
|
||||
1. `http://<IP_ADDPRESS_OF_YOUR_PI_HOLE>/admin/`
|
||||
2. `http://pi.hole/admin/` (when using Pi-hole as your DNS server)
|
||||
3. `http://pi.hole/` (when using Pi-hole as your DNS server)
|
||||
|
||||
The same output can be achieved on the CLI by running `chronometer.sh -j`
|
||||
## Faster-than-light Engine
|
||||
FTLDNS is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTLDNS does this all *very quickly*!
|
||||
|
||||
## Real-time Statistics
|
||||
Some of the statistics you can integrate include:
|
||||
* Total number of domains being blocked
|
||||
* Total number of DNS queries today
|
||||
* Total number of ads blocked today
|
||||
* Percentage of ads blocked
|
||||
* Unique domains
|
||||
* Queries forwarded (to your chosen upstream DNS server)
|
||||
* Queries cached
|
||||
* Unique clients
|
||||
|
||||
You can view [real-time stats](https://discourse.pi-hole.net/t/how-do-i-view-my-pi-holes-stats-over-ssh-or-on-an-lcd-using-chronometer/240) via `ssh` or on an [2.8" LCD screen](http://amzn.to/1P0q1Fj). This is accomplished via [`chronometer.sh`](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/chronometer.sh). 
|
||||
The API can be accessed via [`telnet`](https://github.com/pi-hole/FTL), the Web (`admin/api.php`) and Command Line (`pihole -c -j`). You can out find [more details over here](https://discourse.pi-hole.net/t/pi-hole-api/1863).
|
||||
|
||||
## Pi-hole™ Projects
|
||||
-----
|
||||
|
||||
- [An ad blocking Magic Mirror](https://zonksec.com/blog/magic-mirror-dns-filtering/#dnssoftware)
|
||||
- [Pi-hole stats in your Mac's menu bar](https://getbitbar.com/plugins/Network/pi-hole.1m.py)
|
||||
- [Get LED alerts for each blocked ad](http://thetimmy.silvernight.org/pages/endisbutton/)
|
||||
- [Pi-hole on Ubuntu 14.04 on VirtualBox](http://hbalagtas.blogspot.com/2016/02/adblocking-with-pi-hole-and-ubuntu-1404.html)
|
||||
- [Docker Pi-hole container (x86 and ARM)](https://hub.docker.com/r/diginc/pi-hole/)
|
||||
- [Splunk: Pi-hole Visualiser](https://splunkbase.splunk.com/app/3023/)
|
||||
- [Pi-hole Chrome extension](https://chrome.google.com/webstore/detail/pi-hole-list-editor/hlnoeoejkllgkjbnnnhfolapllcnaglh) ([open source](https://github.com/packtloss/pihole-extension))
|
||||
- [Go Bananas for CHiP-hole ad blocking](https://www.hackster.io/jacobsalmela/chip-hole-network-wide-ad-blocker-98e037)
|
||||
- [Sky-Hole](http://dlaa.me/blog/post/skyhole)
|
||||
- [Pi-hole in the Cloud!](http://blog.codybunch.com/2015/07/28/Pi-Hole-in-the-cloud/)
|
||||
- [unRaid-hole](https://github.com/spants/unraidtemplates/blob/master/Spants/unRaid-hole.xml#L13)--[Repo and more info](http://lime-technology.com/forum/index.php?PHPSESSID=c0eae3e5ef7e521f7866034a3336489d&topic=38486.0)
|
||||
- [Pi-hole on/off button](http://thetimmy.silvernight.org/pages/endisbutton/)
|
||||
- [Minibian Pi-hole](http://munkjensen.net/wiki/index.php/See_my_Pi-Hole#Minibian_Pi-hole)
|
||||
- [Windows Tray Stat Application](https://github.com/goldbattle/copernicus)
|
||||
- [Let your blink1 device blink when Pi-hole filters ads](https://gist.github.com/elpatron68/ec0b4c582e5abf604885ac1e068d233f)
|
||||
- [Pi-hole Prometheus exporter](https://github.com/nlamirault/pihole_exporter): a [Prometheus](https://prometheus.io/) exporter for Pi-hole
|
||||
- [Pi-hole Droid - open source Android client](https://github.com/friimaind/pi-hole-droid)
|
||||
- [Windows DNS Swapper](https://github.com/roots84/DNS-Swapper), see [#1400](https://github.com/pi-hole/pi-hole/issues/1400)
|
||||
## The Origin Of Pi-hole
|
||||
Pi-hole being an **advertising-aware DNS/Web server**, makes use of the following technologies:
|
||||
|
||||
* [`dnsmasq`](http://www.thekelleys.org.uk/dnsmasq/doc.html) - a lightweight DNS and DHCP server
|
||||
* [`curl`](https://curl.haxx.se) - A command line tool for transferring data with URL syntax
|
||||
* [`lighttpd`](https://www.lighttpd.net) - web server designed and optimized for high performance
|
||||
* [`php`](https://secure.php.net) - a popular general-purpose web scripting language
|
||||
* [AdminLTE Dashboard](https://github.com/almasaeed2010/AdminLTE) - premium admin control panel based on Bootstrap 3.x
|
||||
|
||||
While quite outdated at this point, [this original blog post about Pi-hole](https://jacobsalmela.com/2015/06/16/block-millions-ads-network-wide-with-a-raspberry-pi-hole-2-0/) goes into **great detail** about how Pi-hole was originally set up and how it works. Syntactically, it's no longer accurate, but the same basic principles and logic still apply to Pi-hole's current state.
|
||||
|
||||
-----
|
||||
|
||||
## Coverage
|
||||
- [Lifehacker: Turn A Raspberry Pi Into An Ad Blocker With A Single Command](https://www.lifehacker.com.au/2015/02/turn-a-raspberry-pi-into-an-ad-blocker-with-a-single-command/) (Feburary, 2015)
|
||||
- [MakeUseOf: Adblock Everywhere: The Raspberry Pi-Hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/) (March, 2015)
|
||||
- [Catchpoint: Ad-Blocking on Apple iOS9: Valuing the End User Experience](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/) (September, 2015)
|
||||
- [Security Now Netcast: Pi-hole](https://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s) (October, 2015)
|
||||
- [TekThing: Raspberry Pi-Hole Makes Ads Disappear!](https://youtu.be/8Co59HU2gY0?t=2m) (December, 2015)
|
||||
- [Foolish Tech Show](https://youtu.be/bYyena0I9yc?t=2m4s) (December, 2015)
|
||||
- [Block Ads on All Home Devices for $53.18](https://medium.com/@robleathern/block-ads-on-all-home-devices-for-53-18-a5f1ec139693#.gj1xpgr5d) (December, 2015)
|
||||
- [Pi-Hole for Ubuntu 14.04](http://www.boyter.org/2015/12/pi-hole-ubuntu-14-04/) (December, 2015)
|
||||
- [MacObserver Podcast 585](https://www.macobserver.com/tmo/podcast/macgeekgab-585) (December, 2015)
|
||||
- [The Defrag Show: Endoscope USB Camera, The Final [HoloLens] Vote, Adblock Pi and more](https://channel9.msdn.com/Shows/The-Defrag-Show/Defrag-Endoscope-USB-Camera-The-Final-HoloLens-Vote-Adblock-Pi-and-more?WT.mc_id=dlvr_twitter_ch9#time=20m39s) (January, 2016)
|
||||
- [Adafruit: Pi-hole is a black hole for internet ads](https://blog.adafruit.com/2016/03/04/pi-hole-is-a-black-hole-for-internet-ads-piday-raspberrypi-raspberry_pi/) (March, 2016)
|
||||
- [Digital Trends: 5 Fun, Easy Projects You Can Try With a $35 Raspberry Pi](https://youtu.be/QwrKlyC2kdM?t=1m42s) (March, 2016)
|
||||
- [Adafruit: Raspberry Pi Quick Look at Pi Hole ad blocking server with Tony D](https://www.youtube.com/watch?v=eg4u2j1HYlI) (June, 2016)
|
||||
- [Devacron: OrangePi Zero as an Ad-Block server with Pi-Hole](http://www.devacron.com/orangepi-zero-as-an-ad-block-server-with-pi-hole/) (December, 2016)
|
||||
- [Linux Pro: The Hole Truth](http://www.linuxpromagazine.com/Issues/2017/200/The-sysadmin-s-daily-grind-Pi-hole) (July, 2017)
|
||||
- [Adafruit: installing Pi-hole on a Pi Zero W](https://learn.adafruit.com/pi-hole-ad-blocker-with-pi-zero-w/install-pi-hole) (August, 2017)
|
||||
- [CryptoAUSTRALIA: How We Tried 5 Privacy Focused Raspberry Pi Projects](https://blog.cryptoaustralia.org.au/2017/10/05/5-privacy-focused-raspberry-pi-projects/) (October, 2017)
|
||||
- [CryptoAUSTRALIA: Pi-hole Workshop](https://blog.cryptoaustralia.org.au/2017/11/02/pi-hole-network-wide-ad-blocker/) (November, 2017)
|
||||
- [Know How 355: Killing ads with a Raspberry Pi-Hole!](https://www.twit.tv/shows/know-how/episodes/355) (November, 2017)
|
||||
- [Hobohouse: Block Advertising on your Network with Pi-hole and Raspberry Pi](https://hobo.house/2018/02/27/block-advertising-with-pi-hole-and-raspberry-pi/) (March, 2018)
|
||||
- [Scott Helme: Securing DNS across all of my devices with Pi-Hole + DNS-over-HTTPS + 1.1.1.1](https://scotthelme.co.uk/securing-dns-across-all-of-my-devices-with-pihole-dns-over-https-1-1-1-1/) (April, 2018)
|
||||
- [Scott Helme: Catching and dealing with naughty devices on my home network](https://scotthelme.co.uk/catching-naughty-devices-on-my-home-network/) (April, 2018)
|
||||
- [Bloomberg Business Week: Brotherhood of the Ad blockers](https://www.bloomberg.com/news/features/2018-05-10/inside-the-brotherhood-of-pi-hole-ad-blockers) (May, 2018)
|
||||
- [Software Engineering Daily: Interview with the creator of Pi-hole](https://softwareengineeringdaily.com/2018/05/29/pi-hole-ad-blocker-hardware-with-jacob-salmela/) (May, 2018)
|
||||
- [Raspberry Pi: Block ads at home using Pi-hole and a Raspberry Pi](https://www.raspberrypi.org/blog/pi-hole-raspberry-pi/) (July, 2018)
|
||||
- [Troy Hunt: Mmm... Pi-hole...](https://www.troyhunt.com/mmm-pi-hole/) (September, 2018)
|
||||
- [PEBKAK Podcast: Interview With Jacob Salmela](https://www.jerseystudios.net/2018/10/11/150-pi-hole/) (October, 2018)
|
||||
|
||||
- [Adafruit livestream install](https://www.youtube.com/watch?v=eg4u2j1HYlI)
|
||||
- [TekThing: 5 fun, easy projects for a Raspberry Pi](https://youtu.be/QwrKlyC2kdM?t=1m42s)
|
||||
- [Pi-hole on Adafruit's blog](https://blog.adafruit.com/2016/03/04/pi-hole-is-a-black-hole-for-internet-ads-piday-raspberrypi-raspberry_pi/)
|
||||
- [The Defrag Show - MSDN/Channel 9](https://channel9.msdn.com/Shows/The-Defrag-Show/Defrag-Endoscope-USB-Camera-The-Final-HoloLens-Vote-Adblock-Pi-and-more?WT.mc_id=dlvr_twitter_ch9#time=20m39s)
|
||||
- [MacObserver Podcast 585](http://www.macobserver.com/tmo/podcast/macgeekgab-585)
|
||||
- [Medium: Block All Ads For $53](https://medium.com/@robleathern/block-ads-on-all-home-devices-for-53-18-a5f1ec139693#.gj1xpgr5d)
|
||||
- [MakeUseOf: Adblock Everywhere, The Pi-hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/)
|
||||
- [Lifehacker: Turn Your Pi Into An Ad Blocker With A Single Command](http://lifehacker.com/turn-a-raspberry-pi-into-an-ad-blocker-with-a-single-co-1686093533)!
|
||||
- [Pi-hole on TekThing](https://youtu.be/8Co59HU2gY0?t=2m)
|
||||
- [Pi-hole on Security Now! Podcast](http://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s)
|
||||
- [Foolish Tech Show](https://youtu.be/bYyena0I9yc?t=2m4s)
|
||||
- [Pi-hole on Ubuntu](http://www.boyter.org/2015/12/pi-hole-ubuntu-14-04/)
|
||||
- [Catchpoint: iOS 9 Ad Blocking](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/)
|
||||
- [Build an Ad-Blocker for less than 10$ with Orange-Pi](http://www.devacron.com/orangepi-zero-as-an-ad-block-server-with-pi-hole/)
|
||||
-----
|
||||
|
||||
## Pi-hole Projects
|
||||
- [The Big Blocklist Collection](https://wally3k.github.io)
|
||||
- [Pie in the Sky-Hole](https://dlaa.me/blog/post/skyhole)
|
||||
- [Copernicus: Windows Tray Application](https://github.com/goldbattle/copernicus)
|
||||
- [Magic Mirror with DNS Filtering](https://zonksec.com/blog/magic-mirror-dns-filtering/#dnssoftware)
|
||||
- [Windows DNS Swapper](https://github.com/roots84/DNS-Swapper)
|
||||
|
@@ -1,23 +0,0 @@
|
||||
# The below list amalgamates several lists we used previously.
|
||||
# See `https://github.com/StevenBlack/hosts` for details
|
||||
##StevenBlack's list
|
||||
https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
|
||||
|
||||
##MalwareDomains
|
||||
https://mirror1.malwaredomains.com/files/justdomains
|
||||
|
||||
##Cameleon
|
||||
http://sysctl.org/cameleon/hosts
|
||||
|
||||
##Zeustracker
|
||||
https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist
|
||||
|
||||
##Disconnect.me Tracking
|
||||
https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt
|
||||
|
||||
##Disconnect.me Ads
|
||||
https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt
|
||||
|
||||
##Hosts-file.net
|
||||
https://hosts-file.net/ad_servers.txt
|
||||
|
@@ -1,13 +1,11 @@
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2015, 2016 by Jacob Salmela
|
||||
# Network-wide ad blocking via your Raspberry Pi
|
||||
# http://pi-hole.net
|
||||
# dnsmasq config for Pi-hole
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Pi-hole 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.
|
||||
# Dnsmasq config for Pi-hole's FTLDNS
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
###############################################################################
|
||||
# FILE AUTOMATICALLY POPULATED BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
|
||||
@@ -16,13 +14,16 @@
|
||||
# IF YOU WISH TO CHANGE THE UPSTREAM SERVERS, CHANGE THEM IN: #
|
||||
# /etc/pihole/setupVars.conf #
|
||||
# #
|
||||
# ANY OTHER CHANGES SHOULD BE MADE IN A SEPERATE CONFIG FILE #
|
||||
# OR IN /etc/dnsmasq.conf #
|
||||
# ANY OTHER CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE #
|
||||
# WITHIN /etc/dnsmasq.d/yourname.conf #
|
||||
###############################################################################
|
||||
|
||||
addn-hosts=/etc/pihole/gravity.list
|
||||
addn-hosts=/etc/pihole/local.list
|
||||
addn-hosts=/etc/pihole/black.list
|
||||
addn-hosts=/etc/pihole/local.list
|
||||
|
||||
user=pihole
|
||||
group=pihole
|
||||
|
||||
domain-needed
|
||||
|
||||
@@ -42,6 +43,6 @@ cache-size=10000
|
||||
log-queries
|
||||
log-facility=/var/log/pihole.log
|
||||
|
||||
local-ttl=300
|
||||
local-ttl=2
|
||||
|
||||
log-async
|
||||
|
49
advanced/Scripts/COL_TABLE
Normal file
49
advanced/Scripts/COL_TABLE
Normal file
@@ -0,0 +1,49 @@
|
||||
# Determine if terminal is capable of showing colours
|
||||
if [[ -t 1 ]] && [[ $(tput colors) -ge 8 ]]; then
|
||||
# Bold and underline may not show up on all clients
|
||||
# If something MUST be emphasised, use both
|
||||
COL_BOLD='[1m'
|
||||
COL_ULINE='[4m'
|
||||
|
||||
COL_NC='[0m'
|
||||
COL_GRAY='[90m'
|
||||
COL_RED='[91m'
|
||||
COL_GREEN='[32m'
|
||||
COL_YELLOW='[33m'
|
||||
COL_BLUE='[94m'
|
||||
COL_PURPLE='[95m'
|
||||
COL_CYAN='[96m'
|
||||
else
|
||||
# Provide empty variables for `set -u`
|
||||
COL_BOLD=""
|
||||
COL_ULINE=""
|
||||
|
||||
COL_NC=""
|
||||
COL_GRAY=""
|
||||
COL_RED=""
|
||||
COL_GREEN=""
|
||||
COL_YELLOW=""
|
||||
COL_BLUE=""
|
||||
COL_PURPLE=""
|
||||
COL_CYAN=""
|
||||
fi
|
||||
|
||||
# Deprecated variables
|
||||
COL_WHITE="${COL_BOLD}"
|
||||
COL_BLACK="${COL_NC}"
|
||||
COL_LIGHT_BLUE="${COL_BLUE}"
|
||||
COL_LIGHT_GREEN="${COL_GREEN}"
|
||||
COL_LIGHT_CYAN="${COL_CYAN}"
|
||||
COL_LIGHT_RED="${COL_RED}"
|
||||
COL_URG_RED="${COL_RED}${COL_BOLD}${COL_ULINE}"
|
||||
COL_LIGHT_PURPLE="${COL_PURPLE}"
|
||||
COL_BROWN="${COL_YELLOW}"
|
||||
COL_LIGHT_GRAY="${COL_GRAY}"
|
||||
COL_DARK_GRAY="${COL_GRAY}"
|
||||
|
||||
TICK="[${COL_GREEN}✓${COL_NC}]"
|
||||
CROSS="[${COL_RED}✗${COL_NC}]"
|
||||
INFO="[i]"
|
||||
QST="[?]"
|
||||
DONE="${COL_GREEN} done!${COL_NC}"
|
||||
OVER="\\r[K"
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1090,SC1091
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
@@ -7,429 +8,565 @@
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
LC_ALL=C
|
||||
LC_NUMERIC=C
|
||||
|
||||
# Retrieve stats from FTL engine
|
||||
pihole-FTL() {
|
||||
ftl_port=$(cat /var/run/pihole-FTL.port 2> /dev/null)
|
||||
if [[ -n "$ftl_port" ]]; then
|
||||
# Open connection to FTL
|
||||
exec 3<>"/dev/tcp/localhost/$ftl_port"
|
||||
ftl_port=$(cat /var/run/pihole-FTL.port 2> /dev/null)
|
||||
if [[ -n "$ftl_port" ]]; then
|
||||
# Open connection to FTL
|
||||
exec 3<>"/dev/tcp/127.0.0.1/$ftl_port"
|
||||
|
||||
# Test if connection is open
|
||||
if { "true" >&3; } 2> /dev/null; then
|
||||
# Send command to FTL
|
||||
echo -e ">$1" >&3
|
||||
# Test if connection is open
|
||||
if { "true" >&3; } 2> /dev/null; then
|
||||
# Send command to FTL
|
||||
echo -e ">$1" >&3
|
||||
|
||||
# Read input
|
||||
read -r -t 1 LINE <&3
|
||||
until [[ ! $? ]] || [[ "$LINE" == *"EOM"* ]]; do
|
||||
echo "$LINE" >&1
|
||||
read -r -t 1 LINE <&3
|
||||
done
|
||||
# Read input
|
||||
read -r -t 1 LINE <&3
|
||||
until [[ ! $? ]] || [[ "$LINE" == *"EOM"* ]]; do
|
||||
echo "$LINE" >&1
|
||||
read -r -t 1 LINE <&3
|
||||
done
|
||||
|
||||
# Close connection
|
||||
exec 3>&-
|
||||
exec 3<&-
|
||||
fi
|
||||
else
|
||||
echo -e "${COL_LIGHT_RED}FTL offline${COL_NC}"
|
||||
fi
|
||||
# Close connection
|
||||
exec 3>&-
|
||||
exec 3<&-
|
||||
fi
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
# Print spaces to align right-side content
|
||||
# Print spaces to align right-side additional text
|
||||
printFunc() {
|
||||
txt_len="${#2}"
|
||||
|
||||
# Reduce string length when using colour code
|
||||
[ "${2:0:1}" == "" ] && txt_len=$((txt_len-7))
|
||||
|
||||
if [[ "$3" == "last" ]]; then
|
||||
# Prevent final line from printing trailing newline
|
||||
scr_size=( $(stty size 2>/dev/null || echo 24 80) )
|
||||
scr_width="${scr_size[1]}"
|
||||
|
||||
title_len="${#1}"
|
||||
spc_num=$(( (scr_width - title_len) - txt_len ))
|
||||
[[ "$spc_num" -lt 0 ]] && spc_num="0"
|
||||
spc=$(printf "%${spc_num}s")
|
||||
|
||||
printf "%s%s$spc" "$1" "$2"
|
||||
else
|
||||
# Determine number of spaces for padding
|
||||
spc_num=$(( 20 - txt_len ))
|
||||
[[ "$spc_num" -lt 0 ]] && spc_num="0"
|
||||
spc=$(printf "%${spc_num}s")
|
||||
local text_last
|
||||
|
||||
# Print string (Max 20 characters, prevents overflow)
|
||||
printf "%s%s$spc" "$1" "${2:0:20}"
|
||||
fi
|
||||
title="$1"
|
||||
title_len="${#title}"
|
||||
|
||||
text_main="$2"
|
||||
text_main_nocol="$text_main"
|
||||
if [[ "${text_main:0:1}" == "" ]]; then
|
||||
text_main_nocol=$(sed 's/\[[0-9;]\{1,5\}m//g' <<< "$text_main")
|
||||
fi
|
||||
text_main_len="${#text_main_nocol}"
|
||||
|
||||
text_addn="$3"
|
||||
if [[ "$text_addn" == "last" ]]; then
|
||||
text_addn=""
|
||||
text_last="true"
|
||||
fi
|
||||
|
||||
# If there is additional text, define max length of text_main
|
||||
if [[ -n "$text_addn" ]]; then
|
||||
case "$scr_cols" in
|
||||
[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-4]) text_main_max_len="9";;
|
||||
4[5-9]) text_main_max_len="14";;
|
||||
*) text_main_max_len="19";;
|
||||
esac
|
||||
fi
|
||||
|
||||
[[ -z "$text_addn" ]] && text_main_max_len="$(( scr_cols - title_len ))"
|
||||
|
||||
# Remove excess characters from main text
|
||||
if [[ "$text_main_len" -gt "$text_main_max_len" ]]; then
|
||||
# Trim text without colours
|
||||
text_main_trim="${text_main_nocol:0:$text_main_max_len}"
|
||||
# Replace with trimmed text
|
||||
text_main="${text_main/$text_main_nocol/$text_main_trim}"
|
||||
fi
|
||||
|
||||
# Determine amount of spaces for each line
|
||||
if [[ -n "$text_last" ]]; then
|
||||
# Move cursor to end of screen
|
||||
spc_num=$(( scr_cols - ( title_len + text_main_len ) ))
|
||||
else
|
||||
spc_num=$(( text_main_max_len - text_main_len ))
|
||||
fi
|
||||
|
||||
[[ "$spc_num" -le 0 ]] && spc_num="0"
|
||||
spc=$(printf "%${spc_num}s")
|
||||
#spc="${spc// /.}" # Debug: Visualise spaces
|
||||
|
||||
printf "%s%s$spc" "$title" "$text_main"
|
||||
|
||||
if [[ -n "$text_addn" ]]; then
|
||||
printf "%s(%s)%s\\n" "$COL_NC$COL_DARK_GRAY" "$text_addn" "$COL_NC"
|
||||
else
|
||||
# Do not print trailing newline on final line
|
||||
[[ -z "$text_last" ]] && printf "%s\\n" "$COL_NC"
|
||||
fi
|
||||
}
|
||||
|
||||
# Perform on first Chrono run (not for JSON formatted string)
|
||||
get_init_stats() {
|
||||
LC_NUMERIC=C
|
||||
calcFunc(){ awk "BEGIN {print $*}"; }
|
||||
calcFunc(){ awk "BEGIN {print $*}" 2> /dev/null; }
|
||||
|
||||
# Convert bytes to human-readable format
|
||||
hrBytes() {
|
||||
awk '{
|
||||
num=$1;
|
||||
if(num==0) {
|
||||
print "0 B"
|
||||
} else {
|
||||
xxx=(num<0?-num:num)
|
||||
sss=(num<0?-1:1)
|
||||
split("B KB MB GB TB PB",type)
|
||||
for(i=5;yyy < 1;i--) {
|
||||
yyy=xxx / (2^(10*i))
|
||||
}
|
||||
printf "%.0f " type[i+2], yyy*sss
|
||||
}
|
||||
}' <<< "$1";
|
||||
}
|
||||
# Convert bytes to human-readable format
|
||||
hrBytes() {
|
||||
awk '{
|
||||
num=$1;
|
||||
if(num==0) {
|
||||
print "0 B"
|
||||
} else {
|
||||
xxx=(num<0?-num:num)
|
||||
sss=(num<0?-1:1)
|
||||
split("B KB MB GB TB PB",type)
|
||||
for(i=5;yyy < 1;i--) {
|
||||
yyy=xxx / (2^(10*i))
|
||||
}
|
||||
printf "%.0f " type[i+2], yyy*sss
|
||||
}
|
||||
}' <<< "$1";
|
||||
}
|
||||
|
||||
# Convert seconds to human-readable format
|
||||
hrSecs() {
|
||||
day=$(( $1/60/60/24 )); hrs=$(( $1/3600%24 )); mins=$(( ($1%3600)/60 )); secs=$(( $1%60 ))
|
||||
[[ "$day" -ge "2" ]] && plu="s"
|
||||
[[ "$day" -ge "1" ]] && days="$day day${plu}, " || days=""
|
||||
printf "%s%02d:%02d:%02d\n" "$days" "$hrs" "$mins" "$secs"
|
||||
}
|
||||
# Convert seconds to human-readable format
|
||||
hrSecs() {
|
||||
day=$(( $1/60/60/24 )); hrs=$(( $1/3600%24 ))
|
||||
mins=$(( ($1%3600)/60 )); secs=$(( $1%60 ))
|
||||
[[ "$day" -ge "2" ]] && plu="s"
|
||||
[[ "$day" -ge "1" ]] && days="$day day${plu}, " || days=""
|
||||
printf "%s%02d:%02d:%02d\\n" "$days" "$hrs" "$mins" "$secs"
|
||||
}
|
||||
|
||||
# Set Colour Codes
|
||||
coltable="/opt/pihole/COL_TABLE"
|
||||
if [[ -f "${coltable}" ]]; then
|
||||
source ${coltable}
|
||||
else
|
||||
COL_NC='[0m'
|
||||
COL_DARK_GRAY='[1;30m'
|
||||
COL_LIGHT_GREEN='[1;32m'
|
||||
COL_LIGHT_BLUE='[1;34m'
|
||||
COL_LIGHT_RED='[1;31m'
|
||||
COL_YELLOW='[1;33m'
|
||||
COL_LIGHT_RED='[1;31m'
|
||||
COL_URG_RED='[39;41m'
|
||||
fi
|
||||
# Set Colour Codes
|
||||
coltable="/opt/pihole/COL_TABLE"
|
||||
if [[ -f "${coltable}" ]]; then
|
||||
source ${coltable}
|
||||
else
|
||||
COL_NC="[0m"
|
||||
COL_DARK_GRAY="[1;30m"
|
||||
COL_LIGHT_GREEN="[1;32m"
|
||||
COL_LIGHT_BLUE="[1;34m"
|
||||
COL_LIGHT_RED="[1;31m"
|
||||
COL_YELLOW="[1;33m"
|
||||
COL_LIGHT_RED="[1;31m"
|
||||
COL_URG_RED="[39;41m"
|
||||
fi
|
||||
|
||||
# Get RPi model number, or OS distro info
|
||||
if command -v vcgencmd &> /dev/null; then
|
||||
sys_rev=$(awk '/Revision/ {print $3}' < /proc/cpuinfo)
|
||||
case "$sys_rev" in
|
||||
000[2-6]) sys_model=" 1, Model B";; # 256MB
|
||||
000[7-9]) sys_model=" 1, Model A" ;; # 256MB
|
||||
000d|000e|000f) sys_model=" 1, Model B";; # 512MB
|
||||
0010|0013) sys_model=" 1, Model B+";; # 512MB
|
||||
0012|0015) sys_model=" 1, Model A+";; # 256MB
|
||||
a0104[0-1]|a21041|a22042) sys_model=" 2, Model B";; # 1GB
|
||||
900021) sys_model=" 1, Model A+";; # 512MB
|
||||
900032) sys_model=" 1, Model B+";; # 512MB
|
||||
90009[2-3]|920093) sys_model=" Zero";; # 512MB
|
||||
9000c1) sys_model=" Zero W";; # 512MB
|
||||
a02082|a[2-3]2082) sys_model=" 3, Model B";; # 1GB
|
||||
*) sys_model="" ;;
|
||||
esac
|
||||
sys_type="Raspberry Pi$sys_model"
|
||||
else
|
||||
source "/etc/os-release"
|
||||
CODENAME=$(sed 's/[()]//g' <<< "${VERSION/* /}")
|
||||
sys_type="${NAME/ */} ${CODENAME^} $VERSION_ID"
|
||||
fi
|
||||
# Get RPi throttle state (RPi 3B only) & model number, or OS distro info
|
||||
if command -v vcgencmd &> /dev/null; then
|
||||
local sys_throttle_raw
|
||||
local sys_rev_raw
|
||||
|
||||
# Get core count
|
||||
sys_cores=$(grep -c "^processor" /proc/cpuinfo)
|
||||
[[ "$sys_cores" -ne 1 ]] && sys_cores_plu="cores" || sys_cores_plu="core"
|
||||
sys_throttle_raw=$(vgt=$(sudo vcgencmd get_throttled); echo "${vgt##*x}")
|
||||
|
||||
# Test existence of clock speed file for ARM CPU
|
||||
if [[ -f "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" ]]; then
|
||||
scaling_freq_file="/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
|
||||
fi
|
||||
# Active Throttle Notice: http://bit.ly/2gnunOo
|
||||
if [[ "$sys_throttle_raw" != "0" ]]; then
|
||||
case "$sys_throttle_raw" in
|
||||
*0001) thr_type="${COL_YELLOW}Under Voltage";;
|
||||
*0002) thr_type="${COL_LIGHT_BLUE}Arm Freq Cap";;
|
||||
*0003) thr_type="${COL_YELLOW}UV${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_BLUE}AFC";;
|
||||
*0004) thr_type="${COL_LIGHT_RED}Throttled";;
|
||||
*0005) thr_type="${COL_YELLOW}UV${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_RED}TT";;
|
||||
*0006) thr_type="${COL_LIGHT_BLUE}AFC${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_RED}TT";;
|
||||
*0007) thr_type="${COL_YELLOW}UV${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_BLUE}AFC${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_RED}TT";;
|
||||
esac
|
||||
[[ -n "$thr_type" ]] && sys_throttle="$thr_type${COL_DARK_GRAY}"
|
||||
fi
|
||||
|
||||
# Test existence of temperature file
|
||||
if [[ -f "/sys/class/thermal/thermal_zone0/temp" ]]; then
|
||||
temp_file="/sys/class/thermal/thermal_zone0/temp"
|
||||
elif [[ -f "/sys/class/hwmon/hwmon0/temp1_input" ]]; then
|
||||
temp_file="/sys/class/hwmon/hwmon0/temp1_input"
|
||||
else
|
||||
temp_file=""
|
||||
fi
|
||||
sys_rev_raw=$(awk '/Revision/ {print $3}' < /proc/cpuinfo)
|
||||
case "$sys_rev_raw" in
|
||||
000[2-6]) sys_model=" 1, Model B";; # 256MB
|
||||
000[7-9]) sys_model=" 1, Model A";; # 256MB
|
||||
000d|000e|000f) sys_model=" 1, Model B";; # 512MB
|
||||
0010|0013) sys_model=" 1, Model B+";; # 512MB
|
||||
0012|0015) sys_model=" 1, Model A+";; # 256MB
|
||||
a0104[0-1]|a21041|a22042) sys_model=" 2, Model B";; # 1GB
|
||||
900021) sys_model=" 1, Model A+";; # 512MB
|
||||
900032) sys_model=" 1, Model B+";; # 512MB
|
||||
90009[2-3]|920093) sys_model=" Zero";; # 512MB
|
||||
9000c1) sys_model=" Zero W";; # 512MB
|
||||
a02082|a[2-3]2082) sys_model=" 3, Model B";; # 1GB
|
||||
a020d3) sys_model=" 3, Model B+";; # 1GB
|
||||
*) sys_model="";;
|
||||
esac
|
||||
sys_type="Raspberry Pi$sys_model"
|
||||
else
|
||||
source "/etc/os-release"
|
||||
CODENAME=$(sed 's/[()]//g' <<< "${VERSION/* /}")
|
||||
sys_type="${NAME/ */} ${CODENAME^} $VERSION_ID"
|
||||
fi
|
||||
|
||||
# Test existence of setupVars config
|
||||
if [[ -f "/etc/pihole/setupVars.conf" ]]; then
|
||||
setupVars="/etc/pihole/setupVars.conf"
|
||||
fi
|
||||
# Get core count
|
||||
sys_cores=$(grep -c "^processor" /proc/cpuinfo)
|
||||
|
||||
# Test existence of clock speed file for ARM CPU
|
||||
if [[ -f "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" ]]; then
|
||||
scaling_freq_file="/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
|
||||
fi
|
||||
|
||||
# Test existence of temperature file
|
||||
if [[ -f "/sys/class/thermal/thermal_zone0/temp" ]]; then
|
||||
temp_file="/sys/class/thermal/thermal_zone0/temp"
|
||||
elif [[ -f "/sys/class/hwmon/hwmon0/temp1_input" ]]; then
|
||||
temp_file="/sys/class/hwmon/hwmon0/temp1_input"
|
||||
else
|
||||
temp_file=""
|
||||
fi
|
||||
|
||||
# Test existence of setupVars config
|
||||
if [[ -f "/etc/pihole/setupVars.conf" ]]; then
|
||||
setupVars="/etc/pihole/setupVars.conf"
|
||||
fi
|
||||
}
|
||||
|
||||
get_sys_stats() {
|
||||
local ph_ver_raw
|
||||
local cpu_raw
|
||||
local ram_raw
|
||||
local disk_raw
|
||||
local ph_ver_raw
|
||||
local cpu_raw
|
||||
local ram_raw
|
||||
local disk_raw
|
||||
|
||||
# Update every 12 refreshes (Def: every 60s)
|
||||
count=$((count+1))
|
||||
if [[ "$count" == "1" ]] || (( "$count" % 12 == 0 )); then
|
||||
[[ -n "$setupVars" ]] && source "$setupVars"
|
||||
|
||||
|
||||
ph_ver_raw=($(pihole -v -c 2> /dev/null | sed -n 's/^.* v/v/p'))
|
||||
if [[ -n "${ph_ver_raw[0]}" ]]; then
|
||||
ph_core_ver="${ph_ver_raw[0]}"
|
||||
ph_lte_ver="${ph_ver_raw[1]}"
|
||||
ph_ftl_ver="${ph_ver_raw[2]}"
|
||||
else
|
||||
ph_core_ver="${COL_LIGHT_RED}API unavailable${COL_NC}"
|
||||
# Update every 12 refreshes (Def: every 60s)
|
||||
count=$((count+1))
|
||||
if [[ "$count" == "1" ]] || (( "$count" % 12 == 0 )); then
|
||||
# Do not source setupVars if file does not exist
|
||||
[[ -n "$setupVars" ]] && source "$setupVars"
|
||||
|
||||
mapfile -t ph_ver_raw < <(pihole -v -c 2> /dev/null | sed -n 's/^.* v/v/p')
|
||||
if [[ -n "${ph_ver_raw[0]}" ]]; then
|
||||
ph_core_ver="${ph_ver_raw[0]}"
|
||||
ph_lte_ver="${ph_ver_raw[1]}"
|
||||
ph_ftl_ver="${ph_ver_raw[2]}"
|
||||
else
|
||||
ph_core_ver="-1"
|
||||
fi
|
||||
|
||||
sys_name=$(hostname)
|
||||
|
||||
[[ -n "$TEMPERATUREUNIT" ]] && temp_unit="$TEMPERATUREUNIT" || temp_unit="c"
|
||||
|
||||
# Get storage stats for partition mounted on /
|
||||
read -r -a disk_raw <<< "$(df -B1 / 2> /dev/null | awk 'END{ print $3,$2,$5 }')"
|
||||
disk_used="${disk_raw[0]}"
|
||||
disk_total="${disk_raw[1]}"
|
||||
disk_perc="${disk_raw[2]}"
|
||||
|
||||
net_gateway=$(ip route | grep default | cut -d ' ' -f 3 | head -n 1)
|
||||
|
||||
# Get DHCP stats, if feature is enabled
|
||||
if [[ "$DHCP_ACTIVE" == "true" ]]; then
|
||||
ph_dhcp_max=$(( ${DHCP_END##*.} - ${DHCP_START##*.} + 1 ))
|
||||
fi
|
||||
|
||||
# Get DNS server count
|
||||
dns_count="0"
|
||||
[[ -n "${PIHOLE_DNS_1}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_2}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_3}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_4}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_5}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_6}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_7}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_8}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_9}" ]] && dns_count="$dns_count+"
|
||||
fi
|
||||
|
||||
sys_name=$(hostname)
|
||||
|
||||
[[ -n "$TEMPERATUREUNIT" ]] && temp_unit="$TEMPERATUREUNIT" || temp_unit="c"
|
||||
|
||||
# Get storage stats for partition mounted on /
|
||||
disk_raw=($(df -B1 / 2> /dev/null | awk 'END{ print $3,$2,$5 }'))
|
||||
disk_used="${disk_raw[0]}"
|
||||
disk_total="${disk_raw[1]}"
|
||||
disk_perc="${disk_raw[2]}"
|
||||
|
||||
net_gateway=$(route -n | awk '$4 == "UG" {print $2;exit}')
|
||||
|
||||
# Get DHCP stats, if feature is enabled
|
||||
|
||||
# Get screen size
|
||||
read -r -a scr_size <<< "$(stty size 2>/dev/null || echo 24 80)"
|
||||
scr_lines="${scr_size[0]}"
|
||||
scr_cols="${scr_size[1]}"
|
||||
|
||||
# Determine Chronometer size behaviour
|
||||
if [[ "$scr_cols" -ge 58 ]]; then
|
||||
chrono_width="large"
|
||||
elif [[ "$scr_cols" -gt 40 ]]; then
|
||||
chrono_width="medium"
|
||||
else
|
||||
chrono_width="small"
|
||||
fi
|
||||
|
||||
# Determine max length of divider string
|
||||
scr_line_len=$(( scr_cols - 2 ))
|
||||
[[ "$scr_line_len" -ge 58 ]] && scr_line_len="58"
|
||||
scr_line_str=$(printf "%${scr_line_len}s")
|
||||
scr_line_str="${scr_line_str// /—}"
|
||||
|
||||
sys_uptime=$(hrSecs "$(cut -d. -f1 /proc/uptime)")
|
||||
sys_loadavg=$(cut -d " " -f1,2,3 /proc/loadavg)
|
||||
|
||||
# Get CPU usage, only counting processes over 1% as active
|
||||
# shellcheck disable=SC2009
|
||||
cpu_raw=$(ps -eo pcpu,rss --no-headers | grep -E -v " 0")
|
||||
cpu_tasks=$(wc -l <<< "$cpu_raw")
|
||||
cpu_taskact=$(sed -r "/(^ 0.)/d" <<< "$cpu_raw" | wc -l)
|
||||
cpu_perc=$(awk '{sum+=$1} END {printf "%.0f\n", sum/'"$sys_cores"'}' <<< "$cpu_raw")
|
||||
|
||||
# Get CPU clock speed
|
||||
if [[ -n "$scaling_freq_file" ]]; then
|
||||
cpu_mhz=$(( $(< /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq) / 1000 ))
|
||||
else
|
||||
cpu_mhz=$(lscpu | awk -F ":" '/MHz/ {print $2;exit}')
|
||||
cpu_mhz=$(printf "%.0f" "${cpu_mhz//[[:space:]]/}")
|
||||
fi
|
||||
|
||||
# Determine whether to display CPU clock speed as MHz or GHz
|
||||
if [[ -n "$cpu_mhz" ]]; then
|
||||
[[ "$cpu_mhz" -le "999" ]] && cpu_freq="$cpu_mhz MHz" || cpu_freq="$(printf "%.1f" $(calcFunc "$cpu_mhz"/1000)) GHz"
|
||||
[[ "${cpu_freq}" == *".0"* ]] && cpu_freq="${cpu_freq/.0/}"
|
||||
fi
|
||||
|
||||
# Determine colour for temperature
|
||||
if [[ -n "$temp_file" ]]; then
|
||||
if [[ "$temp_unit" == "C" ]]; then
|
||||
cpu_temp=$(printf "%.0fc\\n" "$(calcFunc "$(< $temp_file) / 1000")")
|
||||
|
||||
case "${cpu_temp::-1}" in
|
||||
-*|[0-9]|[1-3][0-9]) cpu_col="$COL_LIGHT_BLUE";;
|
||||
4[0-9]) cpu_col="";;
|
||||
5[0-9]) cpu_col="$COL_YELLOW";;
|
||||
6[0-9]) cpu_col="$COL_LIGHT_RED";;
|
||||
*) cpu_col="$COL_URG_RED";;
|
||||
esac
|
||||
|
||||
# $COL_NC$COL_DARK_GRAY is needed for $COL_URG_RED
|
||||
cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY"
|
||||
|
||||
elif [[ "$temp_unit" == "F" ]]; then
|
||||
cpu_temp=$(printf "%.0ff\\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")")
|
||||
|
||||
case "${cpu_temp::-1}" in
|
||||
-*|[0-9]|[0-9][0-9]) cpu_col="$COL_LIGHT_BLUE";;
|
||||
1[0-1][0-9]) cpu_col="";;
|
||||
1[2-3][0-9]) cpu_col="$COL_YELLOW";;
|
||||
1[4-5][0-9]) cpu_col="$COL_LIGHT_RED";;
|
||||
*) cpu_col="$COL_URG_RED";;
|
||||
esac
|
||||
|
||||
cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY"
|
||||
|
||||
else
|
||||
cpu_temp_str=$(printf " @ %.0fk\\n" "$(calcFunc "($(< $temp_file) / 1000) + 273.15")")
|
||||
fi
|
||||
else
|
||||
cpu_temp_str=""
|
||||
fi
|
||||
|
||||
read -r -a ram_raw <<< "$(awk '/MemTotal:/{total=$2} /MemFree:/{free=$2} /Buffers:/{buffers=$2} /^Cached:/{cached=$2} END {printf "%.0f %.0f %.0f", (total-free-buffers-cached)*100/total, (total-free-buffers-cached)*1024, total*1024}' /proc/meminfo)"
|
||||
ram_perc="${ram_raw[0]}"
|
||||
ram_used="${ram_raw[1]}"
|
||||
ram_total="${ram_raw[2]}"
|
||||
|
||||
if [[ "$(pihole status web 2> /dev/null)" == "1" ]]; then
|
||||
ph_status="${COL_LIGHT_GREEN}Active"
|
||||
else
|
||||
ph_status="${COL_LIGHT_RED}Offline"
|
||||
fi
|
||||
|
||||
if [[ "$DHCP_ACTIVE" == "true" ]]; then
|
||||
ph_dhcp_eip="${DHCP_END##*.}"
|
||||
ph_dhcp_max=$(( ${DHCP_END##*.} - ${DHCP_START##*.} + 1 ))
|
||||
local ph_dhcp_range
|
||||
|
||||
ph_dhcp_range=$(seq -s "|" -f "${DHCP_START%.*}.%g" "${DHCP_START##*.}" "${DHCP_END##*.}")
|
||||
|
||||
# Count dynamic leases from available range, and not static leases
|
||||
ph_dhcp_num=$(grep -cE "$ph_dhcp_range" "/etc/pihole/dhcp.leases")
|
||||
ph_dhcp_percent=$(( ph_dhcp_num * 100 / ph_dhcp_max ))
|
||||
fi
|
||||
|
||||
# Get alt DNS server, or print total count of alt DNS servers
|
||||
if [[ -z "${PIHOLE_DNS_3}" ]]; then
|
||||
ph_alts="${PIHOLE_DNS_2}"
|
||||
else
|
||||
dns_count="0"
|
||||
[[ -n "${PIHOLE_DNS_2}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_3}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_4}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_5}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_6}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_7}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_8}" ]] && dns_count=$((dns_count+1))
|
||||
[[ -n "${PIHOLE_DNS_9}" ]] && dns_count="$dns_count+"
|
||||
ph_alts="${dns_count} others"
|
||||
fi
|
||||
fi
|
||||
|
||||
sys_uptime=$(hrSecs "$(cut -d. -f1 /proc/uptime)")
|
||||
sys_loadavg=$(cut -d " " -f1,2,3 /proc/loadavg)
|
||||
|
||||
# Get CPU usage, only counting processes over 1% CPU as active
|
||||
cpu_raw=$(ps -eo pcpu,rss --no-headers | grep -E -v " 0")
|
||||
cpu_tasks=$(wc -l <<< "$cpu_raw")
|
||||
cpu_taskact=$(sed -r "/(^ 0.)/d" <<< "$cpu_raw" | wc -l)
|
||||
cpu_perc=$(awk '{sum+=$1} END {printf "%.0f\n", sum/'"$sys_cores"'}' <<< "$cpu_raw")
|
||||
|
||||
# Get CPU clock speed
|
||||
if [[ -n "$scaling_freq_file" ]]; then
|
||||
cpu_mhz=$(( $(< /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq) / 1000 ))
|
||||
else
|
||||
cpu_mhz=$(lscpu | awk -F "[ .]+" '/MHz/ {print $4;exit}')
|
||||
fi
|
||||
|
||||
# Determine correct string format for CPU clock speed
|
||||
if [[ -n "$cpu_mhz" ]]; then
|
||||
[[ "$cpu_mhz" -le "999" ]] && cpu_freq="$cpu_mhz MHz" || cpu_freq="$(calcFunc "$cpu_mhz"/1000) Ghz"
|
||||
[[ -n "$cpu_freq" ]] && cpu_freq_str=" @ $cpu_freq" || cpu_freq_str=""
|
||||
fi
|
||||
|
||||
# Determine colour for temperature
|
||||
if [[ -n "$temp_file" ]]; then
|
||||
if [[ "$temp_unit" == "C" ]]; then
|
||||
cpu_temp=$(printf "%'.0fc\n" "$(calcFunc "$(< $temp_file) / 1000")")
|
||||
|
||||
case "${cpu_temp::-1}" in
|
||||
-*|[0-9]|[1-3][0-9]) cpu_col="$COL_LIGHT_BLUE";;
|
||||
4[0-9]) cpu_col="";;
|
||||
5[0-9]) cpu_col="$COL_YELLOW";;
|
||||
6[0-9]) cpu_col="$COL_LIGHT_RED";;
|
||||
*) cpu_col="$COL_URG_RED";;
|
||||
esac
|
||||
|
||||
# $COL_NC$COL_DARK_GRAY is needed for $COL_URG_RED
|
||||
cpu_temp_str=", $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY"
|
||||
|
||||
elif [[ "$temp_unit" == "F" ]]; then
|
||||
cpu_temp=$(printf "%'.0ff\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")")
|
||||
|
||||
case "${cpu_temp::-1}" in
|
||||
-*|[0-9]|[0-9][0-9]) cpu_col="$COL_LIGHT_BLUE";;
|
||||
1[0-1][0-9]) cpu_col="";;
|
||||
1[2-3][0-9]) cpu_col="$COL_YELLOW";;
|
||||
1[4-5][0-9]) cpu_col="$COL_LIGHT_RED";;
|
||||
*) cpu_col="$COL_URG_RED";;
|
||||
esac
|
||||
|
||||
cpu_temp_str=", $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY"
|
||||
|
||||
else
|
||||
cpu_temp_str=$(printf ", %'.0fk\n" "$(calcFunc "($(< $temp_file) / 1000) + 273.15")")
|
||||
fi
|
||||
else
|
||||
cpu_temp_str=""
|
||||
fi
|
||||
|
||||
ram_raw=($(awk '/MemTotal:/{total=$2} /MemFree:/{free=$2} /Buffers:/{buffers=$2} /^Cached:/{cached=$2} END {printf "%.0f %.0f %.0f", (total-free-buffers-cached)*100/total, (total-free-buffers-cached)*1024, total*1024}' /proc/meminfo))
|
||||
ram_perc="${ram_raw[0]}"
|
||||
ram_used="${ram_raw[1]}"
|
||||
ram_total="${ram_raw[2]}"
|
||||
|
||||
if [[ "$(pihole status web 2> /dev/null)" == "1" ]]; then
|
||||
ph_status="${COL_LIGHT_GREEN}Active"
|
||||
else
|
||||
ph_status="${COL_LIGHT_RED}Inactive"
|
||||
fi
|
||||
|
||||
if [[ "$DHCP_ACTIVE" == "true" ]]; then
|
||||
ph_dhcp_num=$(wc -l 2> /dev/null < "/etc/pihole/dhcp.leases")
|
||||
fi
|
||||
}
|
||||
|
||||
get_ftl_stats() {
|
||||
local stats_raw
|
||||
|
||||
stats_raw=($(pihole-FTL "stats"))
|
||||
domains_being_blocked_raw="${stats_raw[1]}"
|
||||
dns_queries_today_raw="${stats_raw[3]}"
|
||||
ads_blocked_today_raw="${stats_raw[5]}"
|
||||
ads_percentage_today_raw="${stats_raw[7]}"
|
||||
local stats_raw
|
||||
|
||||
# Only retrieve these stats when not called from jsonFunc
|
||||
if [[ -z "$1" ]]; then
|
||||
local recent_blocked_raw
|
||||
local top_ad_raw
|
||||
local top_domain_raw
|
||||
local top_client_raw
|
||||
|
||||
domains_being_blocked=$(printf "%'.0f\n" "${domains_being_blocked_raw}")
|
||||
dns_queries_today=$(printf "%'.0f\n" "${dns_queries_today_raw}")
|
||||
ads_blocked_today=$(printf "%'.0f\n" "${ads_blocked_today_raw}")
|
||||
ads_percentage_today=$(printf "%'.0f\n" "${ads_percentage_today_raw}")
|
||||
|
||||
recent_blocked_raw=$(pihole-FTL recentBlocked)
|
||||
top_ad_raw=($(pihole-FTL "top-ads (1)"))
|
||||
top_domain_raw=($(pihole-FTL "top-domains (1)"))
|
||||
top_client_raw=($(pihole-FTL "top-clients (1)"))
|
||||
|
||||
# Limit strings to 40 characters to prevent overflow
|
||||
recent_blocked="${recent_blocked_raw:0:40}"
|
||||
top_ad="${top_ad_raw[2]:0:40}"
|
||||
top_domain="${top_domain_raw[2]:0:40}"
|
||||
[[ "${top_client_raw[3]}" ]] && top_client="${top_client_raw[3]:0:40}" || top_client="${top_client_raw[2]:0:40}"
|
||||
fi
|
||||
mapfile -t stats_raw < <(pihole-FTL "stats")
|
||||
domains_being_blocked_raw="${stats_raw[0]#* }"
|
||||
dns_queries_today_raw="${stats_raw[1]#* }"
|
||||
ads_blocked_today_raw="${stats_raw[2]#* }"
|
||||
ads_percentage_today_raw="${stats_raw[3]#* }"
|
||||
queries_forwarded_raw="${stats_raw[5]#* }"
|
||||
queries_cached_raw="${stats_raw[6]#* }"
|
||||
|
||||
# Only retrieve these stats when not called from jsonFunc
|
||||
if [[ -z "$1" ]]; then
|
||||
local top_ad_raw
|
||||
local top_domain_raw
|
||||
local top_client_raw
|
||||
|
||||
domains_being_blocked=$(printf "%.0f\\n" "${domains_being_blocked_raw}" 2> /dev/null)
|
||||
dns_queries_today=$(printf "%.0f\\n" "${dns_queries_today_raw}")
|
||||
ads_blocked_today=$(printf "%.0f\\n" "${ads_blocked_today_raw}")
|
||||
ads_percentage_today=$(printf "%'.0f\\n" "${ads_percentage_today_raw}")
|
||||
queries_cached_percentage=$(printf "%.0f\\n" "$(calcFunc "$queries_cached_raw * 100 / ( $queries_forwarded_raw + $queries_cached_raw )")")
|
||||
recent_blocked=$(pihole-FTL recentBlocked)
|
||||
read -r -a top_ad_raw <<< "$(pihole-FTL "top-ads (1)")"
|
||||
read -r -a top_domain_raw <<< "$(pihole-FTL "top-domains (1)")"
|
||||
read -r -a top_client_raw <<< "$(pihole-FTL "top-clients (1)")"
|
||||
|
||||
top_ad="${top_ad_raw[2]}"
|
||||
top_domain="${top_domain_raw[2]}"
|
||||
if [[ "${top_client_raw[3]}" ]]; then
|
||||
top_client="${top_client_raw[3]}"
|
||||
else
|
||||
top_client="${top_client_raw[2]}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
get_strings() {
|
||||
# Expand or contract strings depending on screen size
|
||||
if [[ "$chrono_width" == "large" ]]; then
|
||||
phc_str=" ${COL_DARK_GRAY}Core"
|
||||
lte_str=" ${COL_DARK_GRAY}Web"
|
||||
ftl_str=" ${COL_DARK_GRAY}FTL"
|
||||
api_str="${COL_LIGHT_RED}API Offline"
|
||||
|
||||
host_info="$sys_type"
|
||||
sys_info="$sys_throttle"
|
||||
sys_info2="Active: $cpu_taskact of $cpu_tasks tasks"
|
||||
used_str="Used: "
|
||||
leased_str="Leased: "
|
||||
domains_being_blocked=$(printf "%'.0f" "$domains_being_blocked")
|
||||
ads_blocked_today=$(printf "%'.0f" "$ads_blocked_today")
|
||||
dns_queries_today=$(printf "%'.0f" "$dns_queries_today")
|
||||
ph_info="Blocking: $domains_being_blocked sites"
|
||||
total_str="Total: "
|
||||
else
|
||||
phc_str=" ${COL_DARK_GRAY}Core"
|
||||
lte_str=" ${COL_DARK_GRAY}Web"
|
||||
ftl_str=" ${COL_DARK_GRAY}FTL"
|
||||
api_str="${COL_LIGHT_RED}API Down"
|
||||
ph_info="$domains_being_blocked blocked"
|
||||
fi
|
||||
|
||||
[[ "$sys_cores" -ne 1 ]] && sys_cores_txt="${sys_cores}x "
|
||||
cpu_info="$sys_cores_txt$cpu_freq$cpu_temp_str"
|
||||
ram_info="$used_str$(hrBytes "$ram_used") of $(hrBytes "$ram_total")"
|
||||
disk_info="$used_str$(hrBytes "$disk_used") of $(hrBytes "$disk_total")"
|
||||
|
||||
lan_info="Gateway: $net_gateway"
|
||||
dhcp_info="$leased_str$ph_dhcp_num of $ph_dhcp_max"
|
||||
|
||||
ads_info="$total_str$ads_blocked_today of $dns_queries_today"
|
||||
dns_info="$dns_count DNS servers"
|
||||
|
||||
[[ "$recent_blocked" == "0" ]] && recent_blocked="${COL_LIGHT_RED}FTL offline${COL_NC}"
|
||||
}
|
||||
|
||||
chronoFunc() {
|
||||
get_init_stats
|
||||
|
||||
for (( ; ; )); do
|
||||
get_sys_stats
|
||||
get_ftl_stats
|
||||
|
||||
# Do not print LTE/FTL strings if API is unavailable
|
||||
ph_core_str=" ${COL_DARK_GRAY}Pi-hole: $ph_core_ver${COL_NC}"
|
||||
if [[ -n "$ph_lte_ver" ]]; then
|
||||
ph_lte_str=" ${COL_DARK_GRAY}AdminLTE: $ph_lte_ver${COL_NC}"
|
||||
ph_ftl_str=" ${COL_DARK_GRAY}FTL: $ph_ftl_ver${COL_NC}"
|
||||
fi
|
||||
|
||||
clear
|
||||
|
||||
echo -e "[0;1;31;91m|¯[0;1;33;93m¯[0;1;32;92m¯[0;1;32;92m(¯[0;1;36;96m)_[0;1;34;94m_[0;1;35;95m|[0;1;33;93m¯[0;1;31;91m|_ [0;1;32;92m__[0;1;36;96m_|[0;1;31;91m¯[0;1;34;94m|[0;1;35;95m__[0;1;31;91m_[0m$ph_core_str
|
||||
[0;1;33;93m| ¯[0;1;32;92m_[0;1;36;96m/¯[0;1;34;94m|_[0;1;35;95m_[0;1;31;91m| [0;1;33;93m' [0;1;32;92m\/ [0;1;36;96m_ [0;1;34;94m\ [0;1;35;95m/ [0;1;31;91m-[0;1;33;93m_)[0m$ph_lte_str
|
||||
[0;1;32;92m|_[0;1;36;96m| [0;1;34;94m|_[0;1;35;95m| [0;1;33;93m|_[0;1;32;92m||[0;1;36;96m_\[0;1;34;94m__[0;1;35;95m_/[0;1;31;91m_\[0;1;33;93m__[0;1;32;92m_|[0m$ph_ftl_str
|
||||
${COL_DARK_GRAY}——————————————————————————————————————————————————————————${COL_NC}"
|
||||
get_init_stats
|
||||
|
||||
printFunc " Hostname: " "$sys_name"
|
||||
[ -n "$sys_type" ] && printf "%s(%s)%s\n" "$COL_DARK_GRAY" "$sys_type" "$COL_NC" || printf "\n"
|
||||
|
||||
printf "%s\n" " Uptime: $sys_uptime"
|
||||
|
||||
printFunc " Task Load: " "$sys_loadavg"
|
||||
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Active: $cpu_taskact of $cpu_tasks tasks" "$COL_NC"
|
||||
|
||||
printFunc " CPU usage: " "$cpu_perc%"
|
||||
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "$sys_cores $sys_cores_plu$cpu_freq_str$cpu_temp_str" "$COL_NC"
|
||||
|
||||
printFunc " RAM usage: " "$ram_perc%"
|
||||
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Used: $(hrBytes "$ram_used") of $(hrBytes "$ram_total")" "$COL_NC"
|
||||
|
||||
printFunc " HDD usage: " "$disk_perc"
|
||||
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Used: $(hrBytes "$disk_used") of $(hrBytes "$disk_total")" "$COL_NC"
|
||||
|
||||
printFunc " LAN addr: " "${IPV4_ADDRESS/\/*/}"
|
||||
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Gateway: $net_gateway" "$COL_NC"
|
||||
|
||||
if [[ "$DHCP_ACTIVE" == "true" ]]; then
|
||||
printFunc " DHCP: " "$DHCP_START to $ph_dhcp_eip"
|
||||
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Leased: $ph_dhcp_num of $ph_dhcp_max" "$COL_NC"
|
||||
fi
|
||||
|
||||
printFunc " Pi-hole: " "$ph_status"
|
||||
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Blocking: $domains_being_blocked sites" "$COL_NC"
|
||||
|
||||
printFunc " Ads Today: " "$ads_percentage_today%"
|
||||
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "$ads_blocked_today of $dns_queries_today queries" "$COL_NC"
|
||||
|
||||
printFunc " Fwd DNS: " "$PIHOLE_DNS_1"
|
||||
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Alt DNS: $ph_alts" "$COL_NC"
|
||||
|
||||
echo -e " ${COL_DARK_GRAY}——————————————————————————————————————————————————————————${COL_NC}"
|
||||
echo " Recently blocked: $recent_blocked"
|
||||
echo " Top Advertiser: $top_ad"
|
||||
echo " Top Domain: $top_domain"
|
||||
printFunc " Top Client: " "$top_client" "last"
|
||||
|
||||
if [[ "$1" == "exit" ]]; then
|
||||
exit 0
|
||||
else
|
||||
if [[ -n "$1" ]]; then
|
||||
sleep "${1}"
|
||||
else
|
||||
sleep 5
|
||||
fi
|
||||
fi
|
||||
|
||||
done
|
||||
for (( ; ; )); do
|
||||
get_sys_stats
|
||||
get_ftl_stats
|
||||
get_strings
|
||||
|
||||
# Strip excess development version numbers
|
||||
if [[ "$ph_core_ver" != "-1" ]]; then
|
||||
phc_ver_str="$phc_str: ${ph_core_ver%-*}${COL_NC}"
|
||||
lte_ver_str="$lte_str: ${ph_lte_ver%-*}${COL_NC}"
|
||||
ftl_ver_str="$ftl_str: ${ph_ftl_ver%-*}${COL_NC}"
|
||||
else
|
||||
phc_ver_str="$phc_str: $api_str${COL_NC}"
|
||||
fi
|
||||
|
||||
# Get refresh number
|
||||
if [[ "$*" == *"-r"* ]]; then
|
||||
num="$*"
|
||||
num="${num/*-r /}"
|
||||
num="${num/ */}"
|
||||
num_str="Refresh set for every $num seconds"
|
||||
else
|
||||
num_str=""
|
||||
fi
|
||||
|
||||
clear
|
||||
|
||||
# Remove exit message heading on third refresh
|
||||
if [[ "$count" -le 2 ]] && [[ "$*" != *"-e"* ]]; then
|
||||
echo -e " ${COL_LIGHT_GREEN}Pi-hole Chronometer${COL_NC}
|
||||
$num_str
|
||||
${COL_LIGHT_RED}Press Ctrl-C to exit${COL_NC}
|
||||
${COL_DARK_GRAY}$scr_line_str${COL_NC}"
|
||||
else
|
||||
echo -e "[0;1;31;91m|¯[0;1;33;93m¯[0;1;32;92m¯[0;1;32;92m(¯[0;1;36;96m)[0;1;34;94m_[0;1;35;95m|[0;1;33;93m¯[0;1;31;91m|_ [0;1;32;92m__[0;1;36;96m_|[0;1;31;91m¯[0;1;34;94m|[0;1;35;95m__[0;1;31;91m_[0m$phc_ver_str\\n[0;1;33;93m| ¯[0;1;32;92m_[0;1;36;96m/¯[0;1;34;94m|[0;1;35;95m_[0;1;31;91m| [0;1;33;93m' [0;1;32;92m\\/ [0;1;36;96m_ [0;1;34;94m\\ [0;1;35;95m/ [0;1;31;91m-[0;1;33;93m_)[0m$lte_ver_str\\n[0;1;32;92m|_[0;1;36;96m| [0;1;34;94m|_[0;1;35;95m| [0;1;33;93m|_[0;1;32;92m||[0;1;36;96m_\\[0;1;34;94m__[0;1;35;95m_/[0;1;31;91m_\\[0;1;33;93m__[0;1;32;92m_|[0m$ftl_ver_str\\n ${COL_DARK_GRAY}$scr_line_str${COL_NC}"
|
||||
fi
|
||||
|
||||
printFunc " Hostname: " "$sys_name" "$host_info"
|
||||
printFunc " Uptime: " "$sys_uptime" "$sys_info"
|
||||
printFunc " Task Load: " "$sys_loadavg" "$sys_info2"
|
||||
printFunc " CPU usage: " "$cpu_perc%" "$cpu_info"
|
||||
printFunc " RAM usage: " "$ram_perc%" "$ram_info"
|
||||
printFunc " HDD usage: " "$disk_perc" "$disk_info"
|
||||
|
||||
if [[ "$scr_lines" -gt 17 ]] && [[ "$chrono_width" != "small" ]]; then
|
||||
printFunc " LAN addr: " "${IPV4_ADDRESS/\/*/}" "$lan_info"
|
||||
fi
|
||||
|
||||
if [[ "$DHCP_ACTIVE" == "true" ]]; then
|
||||
printFunc "DHCP usage: " "$ph_dhcp_percent%" "$dhcp_info"
|
||||
fi
|
||||
|
||||
printFunc " Pi-hole: " "$ph_status" "$ph_info"
|
||||
printFunc " Ads Today: " "$ads_percentage_today%" "$ads_info"
|
||||
printFunc "Local Qrys: " "$queries_cached_percentage%" "$dns_info"
|
||||
|
||||
printFunc " Blocked: " "$recent_blocked"
|
||||
printFunc "Top Advert: " "$top_ad"
|
||||
|
||||
# Provide more stats on screens with more lines
|
||||
if [[ "$scr_lines" -eq 17 ]]; then
|
||||
if [[ "$DHCP_ACTIVE" == "true" ]]; then
|
||||
printFunc "Top Domain: " "$top_domain" "last"
|
||||
else
|
||||
print_client="true"
|
||||
fi
|
||||
else
|
||||
print_client="true"
|
||||
fi
|
||||
|
||||
if [[ -n "$print_client" ]]; then
|
||||
printFunc "Top Domain: " "$top_domain"
|
||||
printFunc "Top Client: " "$top_client" "last"
|
||||
fi
|
||||
|
||||
# Handle exit/refresh options
|
||||
if [[ "$*" == *"-e"* ]]; then
|
||||
exit 0
|
||||
else
|
||||
if [[ "$*" == *"-r"* ]]; then
|
||||
sleep "$num"
|
||||
else
|
||||
sleep 5
|
||||
fi
|
||||
fi
|
||||
|
||||
done
|
||||
}
|
||||
|
||||
jsonFunc() {
|
||||
get_ftl_stats "json"
|
||||
echo "{\"domains_being_blocked\":${domains_being_blocked_raw},\"dns_queries_today\":${dns_queries_today_raw},\"ads_blocked_today\":${ads_blocked_today_raw},\"ads_percentage_today\":${ads_percentage_today_raw}}"
|
||||
get_ftl_stats "json"
|
||||
echo "{\"domains_being_blocked\":${domains_being_blocked_raw},\"dns_queries_today\":${dns_queries_today_raw},\"ads_blocked_today\":${ads_blocked_today_raw},\"ads_percentage_today\":${ads_percentage_today_raw}}"
|
||||
}
|
||||
|
||||
helpFunc() {
|
||||
if [[ "$1" == "?" ]]; then
|
||||
echo "Unknown option. Please view 'pihole -c --help' for more information"
|
||||
echo "Unknown option. Please view 'pihole -c --help' for more information"
|
||||
else
|
||||
echo "Usage: pihole -c [options]
|
||||
echo "Usage: pihole -c [options]
|
||||
Example: 'pihole -c -j'
|
||||
Calculates stats and displays to an LCD
|
||||
|
||||
|
||||
Options:
|
||||
-j, --json Output stats as JSON formatted string
|
||||
-r, --refresh Set update frequency (in seconds)
|
||||
-e, --exit Output stats and exit witout refreshing
|
||||
-h, --help Display this help text"
|
||||
fi
|
||||
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
chronoFunc
|
||||
chronoFunc
|
||||
fi
|
||||
|
||||
for var in "$@"; do
|
||||
case "$var" in
|
||||
"-j" | "--json" ) jsonFunc;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-r" | "--refresh" ) chronoFunc "$2";;
|
||||
"-e" | "--exit" ) chronoFunc "exit";;
|
||||
* ) helpFunc "?";;
|
||||
esac
|
||||
case "$var" in
|
||||
"-j" | "--json" ) jsonFunc;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-r" | "--refresh" ) chronoFunc "$@";;
|
||||
"-e" | "--exit" ) chronoFunc "$@";;
|
||||
* ) helpFunc "?";;
|
||||
esac
|
||||
done
|
||||
|
@@ -10,31 +10,39 @@
|
||||
|
||||
# Globals
|
||||
basename=pihole
|
||||
piholeDir=/etc/${basename}
|
||||
whitelist=${piholeDir}/whitelist.txt
|
||||
blacklist=${piholeDir}/blacklist.txt
|
||||
readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
|
||||
piholeDir=/etc/"${basename}"
|
||||
whitelist="${piholeDir}"/whitelist.txt
|
||||
blacklist="${piholeDir}"/blacklist.txt
|
||||
|
||||
readonly regexlist="/etc/pihole/regex.list"
|
||||
reload=false
|
||||
addmode=true
|
||||
verbose=true
|
||||
wildcard=false
|
||||
|
||||
domList=()
|
||||
domToRemoveList=()
|
||||
|
||||
listMain=""
|
||||
listAlt=""
|
||||
|
||||
colfile="/opt/pihole/COL_TABLE"
|
||||
source ${colfile}
|
||||
|
||||
|
||||
helpFunc() {
|
||||
if [[ "${listMain}" == "${whitelist}" ]]; then
|
||||
param="w"
|
||||
type="white"
|
||||
elif [[ "${listMain}" == "${wildcardlist}" ]]; then
|
||||
param="wild"
|
||||
type="wildcard black"
|
||||
else
|
||||
param="b"
|
||||
type="black"
|
||||
fi
|
||||
if [[ "${listMain}" == "${whitelist}" ]]; then
|
||||
param="w"
|
||||
type="white"
|
||||
elif [[ "${listMain}" == "${regexlist}" && "${wildcard}" == true ]]; then
|
||||
param="-wild"
|
||||
type="wildcard black"
|
||||
elif [[ "${listMain}" == "${regexlist}" ]]; then
|
||||
param="-regex"
|
||||
type="regex black"
|
||||
else
|
||||
param="b"
|
||||
type="black"
|
||||
fi
|
||||
|
||||
echo "Usage: pihole -${param} [options] <domain> <domain2 ...>
|
||||
Example: 'pihole -${param} site.com', or 'pihole -${param} site1.com site2.com'
|
||||
@@ -45,181 +53,223 @@ Options:
|
||||
-nr, --noreload Update ${type}list without refreshing dnsmasq
|
||||
-q, --quiet Make output less verbose
|
||||
-h, --help Show this help dialog
|
||||
-l, --list Display all your ${type}listed domains"
|
||||
-l, --list Display all your ${type}listed domains
|
||||
--nuke Removes all entries in a list"
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
EscapeRegexp() {
|
||||
# This way we may safely insert an arbitrary
|
||||
# string in our regular expressions
|
||||
# Also remove leading "." if present
|
||||
echo $* | sed 's/^\.*//' | sed "s/[]\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g"
|
||||
# This way we may safely insert an arbitrary
|
||||
# string in our regular expressions
|
||||
# This sed is intentionally executed in three steps to ease maintainability
|
||||
# The first sed removes any amount of leading dots
|
||||
echo $* | sed 's/^\.*//' | sed "s/[]\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g"
|
||||
}
|
||||
|
||||
HandleOther() {
|
||||
# First, convert everything to lowercase
|
||||
domain=$(sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" <<< "$1")
|
||||
# Convert to lowercase
|
||||
domain="${1,,}"
|
||||
|
||||
# Check validity of domain
|
||||
validDomain=$(echo "${domain}" | perl -lne 'print if /(?!.*[^a-z0-9-\.].*)^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9-]+\.)*[a-z]{2,63}/')
|
||||
if [[ -z "${validDomain}" ]]; then
|
||||
echo "::: $1 is not a valid argument or domain name"
|
||||
else
|
||||
domList=("${domList[@]}" ${validDomain})
|
||||
fi
|
||||
# Check validity of domain (don't check for regex entries)
|
||||
if [[ "${#domain}" -le 253 ]]; then
|
||||
if [[ "${listMain}" == "${regexlist}" && "${wildcard}" == false ]]; then
|
||||
validDomain="${domain}"
|
||||
else
|
||||
validDomain=$(grep -P "^((-|_)*[a-z\\d]((-|_)*[a-z\\d])*(-|_)*)(\\.(-|_)*([a-z\\d]((-|_)*[a-z\\d])*))*$" <<< "${domain}") # Valid chars check
|
||||
validDomain=$(grep -P "^[^\\.]{1,63}(\\.[^\\.]{1,63})*$" <<< "${validDomain}") # Length of each label
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${validDomain}" ]]; then
|
||||
domList=("${domList[@]}" ${validDomain})
|
||||
else
|
||||
echo -e " ${CROSS} ${domain} is not a valid argument or domain name!"
|
||||
fi
|
||||
}
|
||||
|
||||
PoplistFile() {
|
||||
# Check whitelist file exists, and if not, create it
|
||||
if [[ ! -f ${whitelist} ]]; then
|
||||
touch ${whitelist}
|
||||
fi
|
||||
|
||||
for dom in "${domList[@]}"; do
|
||||
# Logic: If addmode then add to desired list and remove from the other; if delmode then remove from desired list but do not add to the other
|
||||
if ${addmode}; then
|
||||
AddDomain "${dom}" "${listMain}"
|
||||
RemoveDomain "${dom}" "${listAlt}"
|
||||
if [[ "${listMain}" == "${whitelist}" || "${listMain}" == "${blacklist}" ]]; then
|
||||
RemoveDomain "${dom}" "${wildcardlist}"
|
||||
fi
|
||||
else
|
||||
RemoveDomain "${dom}" "${listMain}"
|
||||
# Check whitelist file exists, and if not, create it
|
||||
if [[ ! -f "${whitelist}" ]]; then
|
||||
touch "${whitelist}"
|
||||
fi
|
||||
|
||||
# Check blacklist file exists, and if not, create it
|
||||
if [[ ! -f "${blacklist}" ]]; then
|
||||
touch "${blacklist}"
|
||||
fi
|
||||
|
||||
for dom in "${domList[@]}"; do
|
||||
# Logic: If addmode then add to desired list and remove from the other; if delmode then remove from desired list but do not add to the other
|
||||
if ${addmode}; then
|
||||
AddDomain "${dom}" "${listMain}"
|
||||
RemoveDomain "${dom}" "${listAlt}"
|
||||
else
|
||||
RemoveDomain "${dom}" "${listMain}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
AddDomain() {
|
||||
list="$2"
|
||||
domain=$(EscapeRegexp "$1")
|
||||
list="$2"
|
||||
domain=$(EscapeRegexp "$1")
|
||||
|
||||
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
|
||||
bool=true
|
||||
# Is the domain in the list we want to add it to?
|
||||
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
|
||||
[[ "${list}" == "${whitelist}" ]] && listname="whitelist"
|
||||
[[ "${list}" == "${blacklist}" ]] && listname="blacklist"
|
||||
|
||||
if [[ "${bool}" == false ]]; then
|
||||
# Domain not found in the whitelist file, add it!
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: Adding $1 to $list..."
|
||||
fi
|
||||
reload=true
|
||||
# Add it to the list we want to add it to
|
||||
echo "$1" >> "${list}"
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: ${1} already exists in ${list}, no need to add!"
|
||||
fi
|
||||
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
|
||||
[[ "${list}" == "${whitelist}" && -z "${type}" ]] && type="--whitelist-only"
|
||||
[[ "${list}" == "${blacklist}" && -z "${type}" ]] && type="--blacklist-only"
|
||||
bool=true
|
||||
# Is the domain in the list we want to add it to?
|
||||
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
|
||||
|
||||
if [[ "${bool}" == false ]]; then
|
||||
# Domain not found in the whitelist file, add it!
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo -e " ${INFO} Adding ${1} to ${listname}..."
|
||||
fi
|
||||
reload=true
|
||||
# Add it to the list we want to add it to
|
||||
echo "$1" >> "${list}"
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo -e " ${INFO} ${1} already exists in ${listname}, no need to add!"
|
||||
fi
|
||||
fi
|
||||
elif [[ "${list}" == "${regexlist}" ]]; then
|
||||
[[ -z "${type}" ]] && type="--wildcard-only"
|
||||
bool=true
|
||||
domain="${1}"
|
||||
|
||||
[[ "${wildcard}" == true ]] && domain="(^|\\.)${domain//\./\\.}$"
|
||||
|
||||
# Is the domain in the list?
|
||||
# Search only for exactly matching lines
|
||||
grep -Fx "${domain}" "${regexlist}" > /dev/null 2>&1 || bool=false
|
||||
|
||||
if [[ "${bool}" == false ]]; then
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo -e " ${INFO} Adding ${domain} to regex list..."
|
||||
fi
|
||||
reload="restart"
|
||||
echo "$domain" >> "${regexlist}"
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo -e " ${INFO} ${domain} already exists in regex list, no need to add!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
elif [[ "${list}" == "${wildcardlist}" ]]; then
|
||||
source "${piholeDir}/setupVars.conf"
|
||||
# Remove the /* from the end of the IPv4addr.
|
||||
IPV4_ADDRESS=${IPV4_ADDRESS%/*}
|
||||
IPV6_ADDRESS=${IPV6_ADDRESS}
|
||||
|
||||
bool=true
|
||||
# Is the domain in the list?
|
||||
grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false
|
||||
|
||||
if [[ "${bool}" == false ]]; then
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: Adding $1 to wildcard blacklist..."
|
||||
fi
|
||||
reload=true
|
||||
echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}"
|
||||
if [[ "${#IPV6_ADDRESS}" > 0 ]]; then
|
||||
echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}"
|
||||
fi
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: ${1} already exists in wildcard blacklist, no need to add!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
RemoveDomain() {
|
||||
list="$2"
|
||||
domain=$(EscapeRegexp "$1")
|
||||
|
||||
[[ "${list}" == "${whitelist}" ]] && listname="whitelist"
|
||||
[[ "${list}" == "${blacklist}" ]] && listname="blacklist"
|
||||
|
||||
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
|
||||
bool=true
|
||||
# Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa
|
||||
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
|
||||
if [[ "${bool}" == true ]]; then
|
||||
# Remove it from the other one
|
||||
echo "::: Removing $1 from $list..."
|
||||
# /I flag: search case-insensitive
|
||||
sed -i "/${domain}/Id" "${list}"
|
||||
reload=true
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: ${1} does not exist in ${list}, no need to remove!"
|
||||
bool=true
|
||||
[[ "${list}" == "${whitelist}" && -z "${type}" ]] && type="--whitelist-only"
|
||||
[[ "${list}" == "${blacklist}" && -z "${type}" ]] && type="--blacklist-only"
|
||||
# Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa
|
||||
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
|
||||
if [[ "${bool}" == true ]]; then
|
||||
# Remove it from the other one
|
||||
echo -e " ${INFO} Removing $1 from ${listname}..."
|
||||
# /I flag: search case-insensitive
|
||||
sed -i "/${domain}/Id" "${list}"
|
||||
reload=true
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo -e " ${INFO} ${1} does not exist in ${listname}, no need to remove!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
elif [[ "${list}" == "${wildcardlist}" ]]; then
|
||||
bool=true
|
||||
# Is it in the list?
|
||||
grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false
|
||||
if [[ "${bool}" == true ]]; then
|
||||
# Remove it from the other one
|
||||
echo "::: Removing $1 from $list..."
|
||||
# /I flag: search case-insensitive
|
||||
sed -i "/address=\/${domain}/Id" "${list}"
|
||||
reload=true
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo "::: ${1} does not exist in ${list}, no need to remove!"
|
||||
elif [[ "${list}" == "${regexlist}" ]]; then
|
||||
[[ -z "${type}" ]] && type="--wildcard-only"
|
||||
domain="${1}"
|
||||
|
||||
[[ "${wildcard}" == true ]] && domain="(^|\\.)${domain//\./\\.}$"
|
||||
|
||||
bool=true
|
||||
# Is it in the list?
|
||||
grep -Fx "${domain}" "${regexlist}" > /dev/null 2>&1 || bool=false
|
||||
if [[ "${bool}" == true ]]; then
|
||||
# Remove it from the other one
|
||||
echo -e " ${INFO} Removing $domain from regex list..."
|
||||
local lineNumber
|
||||
lineNumber=$(grep -Fnx "$domain" "${list}" | cut -f1 -d:)
|
||||
sed -i "${lineNumber}d" "${list}"
|
||||
reload=true
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo -e " ${INFO} ${domain} does not exist in regex list, no need to remove!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Update Gravity
|
||||
Reload() {
|
||||
# Reload hosts file
|
||||
pihole -g -sd
|
||||
echo ""
|
||||
pihole -g --skip-download "${type:-}"
|
||||
}
|
||||
|
||||
Displaylist() {
|
||||
if [[ "${listMain}" == "${whitelist}" ]]; then
|
||||
string="gravity resistant domains"
|
||||
else
|
||||
string="domains caught in the sinkhole"
|
||||
fi
|
||||
verbose=false
|
||||
echo -e "Displaying $string:\n"
|
||||
count=1
|
||||
while IFS= read -r RD; do
|
||||
echo "${count}: ${RD}"
|
||||
count=$((count+1))
|
||||
done < "${listMain}"
|
||||
exit 0;
|
||||
if [[ -f ${listMain} ]]; then
|
||||
if [[ "${listMain}" == "${whitelist}" ]]; then
|
||||
string="gravity resistant domains"
|
||||
else
|
||||
string="domains caught in the sinkhole"
|
||||
fi
|
||||
verbose=false
|
||||
echo -e "Displaying $string:\n"
|
||||
count=1
|
||||
while IFS= read -r RD || [ -n "${RD}" ]; do
|
||||
echo " ${count}: ${RD}"
|
||||
count=$((count+1))
|
||||
done < "${listMain}"
|
||||
else
|
||||
echo -e " ${COL_LIGHT_RED}${listMain} does not exist!${COL_NC}"
|
||||
fi
|
||||
exit 0;
|
||||
}
|
||||
|
||||
NukeList() {
|
||||
if [[ -f "${listMain}" ]]; then
|
||||
# Back up original list
|
||||
cp "${listMain}" "${listMain}.bck~"
|
||||
# Empty out file
|
||||
echo "" > "${listMain}"
|
||||
fi
|
||||
}
|
||||
|
||||
for var in "$@"; do
|
||||
case "${var}" in
|
||||
"-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";;
|
||||
"-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";;
|
||||
"-wild" | "wildcard" ) listMain="${wildcardlist}";;
|
||||
"-nr"| "--noreload" ) reload=false;;
|
||||
"-d" | "--delmode" ) addmode=false;;
|
||||
"-f" | "--force" ) force=true;;
|
||||
"-q" | "--quiet" ) verbose=false;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-l" | "--list" ) Displaylist;;
|
||||
* ) HandleOther "${var}";;
|
||||
esac
|
||||
case "${var}" in
|
||||
"-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";;
|
||||
"-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";;
|
||||
"--wild" | "wildcard" ) listMain="${regexlist}"; wildcard=true;;
|
||||
"--regex" | "regex" ) listMain="${regexlist}";;
|
||||
"-nr"| "--noreload" ) reload=false;;
|
||||
"-d" | "--delmode" ) addmode=false;;
|
||||
"-q" | "--quiet" ) verbose=false;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-l" | "--list" ) Displaylist;;
|
||||
"--nuke" ) NukeList;;
|
||||
* ) HandleOther "${var}";;
|
||||
esac
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
helpFunc
|
||||
helpFunc
|
||||
fi
|
||||
|
||||
PoplistFile
|
||||
|
||||
if ${reload}; then
|
||||
Reload
|
||||
if [[ "${reload}" != false ]]; then
|
||||
# Ensure that "restart" is used for Wildcard updates
|
||||
Reload "${reload}"
|
||||
fi
|
||||
|
@@ -3,204 +3,193 @@
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Switch Pi-hole subsystems to a different Github branch
|
||||
# Switch Pi-hole subsystems to a different Github branch.
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
readonly PI_HOLE_FILES_DIR="/etc/.pihole"
|
||||
PH_TEST="true" source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
|
||||
PH_TEST="true"
|
||||
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
|
||||
|
||||
# webInterfaceGitUrl set in basic-install.sh
|
||||
# webInterfaceDir set in basic-install.sh
|
||||
# piholeGitURL set in basic-install.sh
|
||||
# is_repo() sourced from basic-install.sh
|
||||
# setupVars set in basic-install.sh
|
||||
# check_download_exists sourced from basic-install.sh
|
||||
# fully_fetch_repo sourced from basic-install.sh
|
||||
# get_available_branches sourced from basic-install.sh
|
||||
# fetch_checkout_pull_branch sourced from basic-install.sh
|
||||
# checkout_pull_branch sourced from basic-install.sh
|
||||
|
||||
source "${setupVars}"
|
||||
update="false"
|
||||
|
||||
# Colour codes
|
||||
red="\e[1;31m"
|
||||
def="\e[0m"
|
||||
|
||||
fully_fetch_repo() {
|
||||
# Add upstream branches to shallow clone
|
||||
local directory="${1}"
|
||||
|
||||
cd "${directory}" || return 1
|
||||
if is_repo "${directory}"; then
|
||||
git remote set-branches origin '*' || return 1
|
||||
git fetch --quiet || return 1
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
get_available_branches() {
|
||||
# Return available branches
|
||||
local directory="${1}"
|
||||
|
||||
cd "${directory}" || return 1
|
||||
# Get reachable remote branches
|
||||
git remote show origin | grep 'tracked' | sed 's/tracked//;s/ //g'
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
fetch_checkout_pull_branch() {
|
||||
# Check out specified branch
|
||||
local directory="${1}"
|
||||
local branch="${2}"
|
||||
|
||||
# Set the reference for the requested branch, fetch, check it put and pull it
|
||||
cd "${directory}"
|
||||
git remote set-branches origin "${branch}" || return 1
|
||||
git stash --all --quiet &> /dev/null || true
|
||||
git clean --force -d || true
|
||||
git fetch --quiet || return 1
|
||||
checkout_pull_branch "${directory}" "${branch}" || return 1
|
||||
}
|
||||
|
||||
checkout_pull_branch() {
|
||||
# Check out specified branch
|
||||
local directory="${1}"
|
||||
local branch="${2}"
|
||||
local oldbranch
|
||||
|
||||
cd "${directory}" || return 1
|
||||
|
||||
oldbranch="$(git symbolic-ref HEAD)"
|
||||
|
||||
git checkout "${branch}" || return 1
|
||||
|
||||
if [ "$(git diff "${oldbranch}" | grep -c "^")" -gt "0" ]; then
|
||||
update="true"
|
||||
fi
|
||||
|
||||
git pull || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
warning1() {
|
||||
echo " Please note that changing branches severely alters your Pi-hole subsystems"
|
||||
echo " Features that work on the master branch, may not on a development branch"
|
||||
echo -e " ${red}This feature is NOT supported unless a Pi-hole developer explicitly asks!${def}"
|
||||
read -r -p " Have you read and understood this? [y/N] " response
|
||||
case ${response} in
|
||||
[yY][eE][sS]|[yY])
|
||||
echo "::: Continuing with branch change."
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
echo "::: Branch change has been cancelled."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
echo " Please note that changing branches severely alters your Pi-hole subsystems"
|
||||
echo " Features that work on the master branch, may not on a development branch"
|
||||
echo -e " ${COL_LIGHT_RED}This feature is NOT supported unless a Pi-hole developer explicitly asks!${COL_NC}"
|
||||
read -r -p " Have you read and understood this? [y/N] " response
|
||||
case "${response}" in
|
||||
[yY][eE][sS]|[yY])
|
||||
echo ""
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
echo -e "\\n ${INFO} Branch change has been cancelled"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
checkout() {
|
||||
local corebranches
|
||||
local webbranches
|
||||
local corebranches
|
||||
local webbranches
|
||||
|
||||
# Avoid globbing
|
||||
set -f
|
||||
# Avoid globbing
|
||||
set -f
|
||||
|
||||
#This is unlikely
|
||||
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
|
||||
echo "::: Critical Error: Core Pi-hole repo is missing from system!"
|
||||
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole"
|
||||
exit 1;
|
||||
fi
|
||||
if [[ ${INSTALL_WEB} == "true" ]]; then
|
||||
if ! is_repo "${webInterfaceDir}" ; then
|
||||
echo "::: Critical Error: Web Admin repo is missing from system!"
|
||||
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole"
|
||||
exit 1;
|
||||
# This is unlikely
|
||||
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
|
||||
echo -e " ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!"
|
||||
echo -e " Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
if [[ "${INSTALL_WEB_INTERFACE}" == "true" ]]; then
|
||||
if ! is_repo "${webInterfaceDir}" ; then
|
||||
echo -e " ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!"
|
||||
echo -e " Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "${1}" ]]; then
|
||||
echo "::: No option detected. Please use 'pihole checkout <master|dev>'."
|
||||
echo "::: Or enter the repository and branch you would like to check out:"
|
||||
echo "::: 'pihole checkout <web|core> <branchname>'"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${1}" ]]; then
|
||||
echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC}"
|
||||
echo -e " Try 'pihole checkout --help' for more information."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! warning1 ; then
|
||||
exit 1
|
||||
fi
|
||||
if ! warning1 ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${1}" == "dev" ]] ; then
|
||||
# Shortcut to check out development branches
|
||||
echo "::: Shortcut \"dev\" detected - checking out development / devel branches ..."
|
||||
echo "::: Pi-hole core"
|
||||
fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "development" || { echo "Unable to pull Core developement branch"; exit 1; }
|
||||
if [[ ${INSTALL_WEB} == "true" ]]; then
|
||||
echo "::: Web interface"
|
||||
fetch_checkout_pull_branch "${webInterfaceDir}" "devel" || { echo "Unable to pull Web development branch"; exit 1; }
|
||||
fi
|
||||
echo "::: done!"
|
||||
elif [[ "${1}" == "master" ]] ; then
|
||||
# Shortcut to check out master branches
|
||||
echo "::: Shortcut \"master\" detected - checking out master branches ..."
|
||||
echo "::: Pi-hole core"
|
||||
fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "master" || { echo "Unable to pull Core master branch"; exit 1; }
|
||||
if [[ ${INSTALL_WEB} == "true" ]]; then
|
||||
echo "::: Web interface"
|
||||
fetch_checkout_pull_branch "${webInterfaceDir}" "master" || { echo "Unable to pull web master branch"; exit 1; }
|
||||
fi
|
||||
echo "::: done!"
|
||||
elif [[ "${1}" == "core" ]] ; then
|
||||
echo -n "::: Fetching remote branches for Pi-hole core from ${piholeGitUrl} ... "
|
||||
if ! fully_fetch_repo "${PI_HOLE_FILES_DIR}" ; then
|
||||
echo "::: Fetching all branches for Pi-hole core repo failed!"
|
||||
exit 1
|
||||
fi
|
||||
corebranches=($(get_available_branches "${PI_HOLE_FILES_DIR}"))
|
||||
echo " done!"
|
||||
echo "::: ${#corebranches[@]} branches available"
|
||||
echo ":::"
|
||||
# Have to user chosing the branch he wants
|
||||
if ! (for e in "${corebranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then
|
||||
echo "::: Requested branch \"${2}\" is not available!"
|
||||
echo "::: Available branches for core are:"
|
||||
for e in "${corebranches[@]}"; do echo "::: $e"; done
|
||||
exit 1
|
||||
fi
|
||||
checkout_pull_branch "${PI_HOLE_FILES_DIR}" "${2}"
|
||||
elif [[ "${1}" == "web" && "${INSTALL_WEB}" == "true" ]] ; then
|
||||
echo -n "::: Fetching remote branches for the web interface from ${webInterfaceGitUrl} ... "
|
||||
if ! fully_fetch_repo "${webInterfaceDir}" ; then
|
||||
echo "::: Fetching all branches for Pi-hole web interface repo failed!"
|
||||
exit 1
|
||||
fi
|
||||
webbranches=($(get_available_branches "${webInterfaceDir}"))
|
||||
echo " done!"
|
||||
echo "::: ${#webbranches[@]} branches available"
|
||||
echo ":::"
|
||||
# Have to user chosing the branch he wants
|
||||
if ! (for e in "${webbranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then
|
||||
echo "::: Requested branch \"${2}\" is not available!"
|
||||
echo "::: Available branches for web are:"
|
||||
for e in "${webbranches[@]}"; do echo "::: $e"; done
|
||||
exit 1
|
||||
fi
|
||||
checkout_pull_branch "${webInterfaceDir}" "${2}"
|
||||
else
|
||||
echo "::: Requested option \"${1}\" is not available!"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${1}" == "dev" ]] ; then
|
||||
# Shortcut to check out development branches
|
||||
echo -e " ${INFO} Shortcut \"dev\" detected - checking out development / devel branches..."
|
||||
echo ""
|
||||
echo -e " ${INFO} Pi-hole Core"
|
||||
fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "development" || { echo " ${CROSS} Unable to pull Core developement branch"; exit 1; }
|
||||
if [[ "${INSTALL_WEB_INTERFACE}" == "true" ]]; then
|
||||
echo ""
|
||||
echo -e " ${INFO} Web interface"
|
||||
fetch_checkout_pull_branch "${webInterfaceDir}" "devel" || { echo " ${CROSS} Unable to pull Web development branch"; exit 1; }
|
||||
fi
|
||||
#echo -e " ${TICK} Pi-hole Core"
|
||||
|
||||
get_binary_name
|
||||
local path
|
||||
path="development/${binary}"
|
||||
echo "development" > /etc/pihole/ftlbranch
|
||||
elif [[ "${1}" == "master" ]] ; then
|
||||
# Shortcut to check out master branches
|
||||
echo -e " ${INFO} Shortcut \"master\" detected - checking out master branches..."
|
||||
echo -e " ${INFO} Pi-hole core"
|
||||
fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "master" || { echo " ${CROSS} Unable to pull Core master branch"; exit 1; }
|
||||
if [[ ${INSTALL_WEB_INTERFACE} == "true" ]]; then
|
||||
echo -e " ${INFO} Web interface"
|
||||
fetch_checkout_pull_branch "${webInterfaceDir}" "master" || { echo " ${CROSS} Unable to pull Web master branch"; exit 1; }
|
||||
fi
|
||||
#echo -e " ${TICK} Web Interface"
|
||||
get_binary_name
|
||||
local path
|
||||
path="master/${binary}"
|
||||
echo "master" > /etc/pihole/ftlbranch
|
||||
elif [[ "${1}" == "core" ]] ; then
|
||||
str="Fetching branches from ${piholeGitUrl}"
|
||||
echo -ne " ${INFO} $str"
|
||||
if ! fully_fetch_repo "${PI_HOLE_FILES_DIR}" ; then
|
||||
echo -e "${OVER} ${CROSS} $str"
|
||||
exit 1
|
||||
fi
|
||||
corebranches=($(get_available_branches "${PI_HOLE_FILES_DIR}"))
|
||||
|
||||
if [[ "${corebranches[*]}" == *"master"* ]]; then
|
||||
echo -e "${OVER} ${TICK} $str"
|
||||
echo -e "${INFO} ${#corebranches[@]} branches available for Pi-hole Core"
|
||||
else
|
||||
# Print STDERR output from get_available_branches
|
||||
echo -e "${OVER} ${CROSS} $str\\n\\n${corebranches[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
# Have the user choose the branch they want
|
||||
if ! (for e in "${corebranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then
|
||||
echo -e " ${INFO} Requested branch \"${2}\" is not available"
|
||||
echo -e " ${INFO} Available branches for Core are:"
|
||||
for e in "${corebranches[@]}"; do echo " - $e"; done
|
||||
exit 1
|
||||
fi
|
||||
checkout_pull_branch "${PI_HOLE_FILES_DIR}" "${2}"
|
||||
elif [[ "${1}" == "web" ]] && [[ "${INSTALL_WEB_INTERFACE}" == "true" ]] ; then
|
||||
str="Fetching branches from ${webInterfaceGitUrl}"
|
||||
echo -ne " ${INFO} $str"
|
||||
if ! fully_fetch_repo "${webInterfaceDir}" ; then
|
||||
echo -e "${OVER} ${CROSS} $str"
|
||||
exit 1
|
||||
fi
|
||||
webbranches=($(get_available_branches "${webInterfaceDir}"))
|
||||
|
||||
if [[ "${webbranches[*]}" == *"master"* ]]; then
|
||||
echo -e "${OVER} ${TICK} $str"
|
||||
echo -e "${INFO} ${#webbranches[@]} branches available for Web Admin"
|
||||
else
|
||||
# Print STDERR output from get_available_branches
|
||||
echo -e "${OVER} ${CROSS} $str\\n\\n${webbranches[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
# Have the user choose the branch they want
|
||||
if ! (for e in "${webbranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then
|
||||
echo -e " ${INFO} Requested branch \"${2}\" is not available"
|
||||
echo -e " ${INFO} Available branches for Web Admin are:"
|
||||
for e in "${webbranches[@]}"; do echo " - $e"; done
|
||||
exit 1
|
||||
fi
|
||||
checkout_pull_branch "${webInterfaceDir}" "${2}"
|
||||
elif [[ "${1}" == "ftl" ]] ; then
|
||||
get_binary_name
|
||||
local path
|
||||
path="${2}/${binary}"
|
||||
|
||||
if check_download_exists "$path"; then
|
||||
echo " ${TICK} Branch ${2} exists"
|
||||
echo "${2}" > /etc/pihole/ftlbranch
|
||||
FTLinstall "${binary}"
|
||||
start_service pihole-FTL
|
||||
enable_service pihole-FTL
|
||||
else
|
||||
echo " ${CROSS} Requested branch \"${2}\" is not available"
|
||||
ftlbranches=( $(git ls-remote https://github.com/pi-hole/ftl | grep 'heads' | sed 's/refs\/heads\///;s/ //g' | awk '{print $2}') )
|
||||
echo -e " ${INFO} Available branches for FTL are:"
|
||||
for e in "${ftlbranches[@]}"; do echo " - $e"; done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Force updating everything
|
||||
if [[ ! "${1}" == "web" && "${update}" == "true" ]]; then
|
||||
echo "::: Running installer to upgrade your installation"
|
||||
if "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" --unattended; then
|
||||
exit 0
|
||||
else
|
||||
echo "Unable to complete update, contact Pi-hole"
|
||||
exit 1
|
||||
echo -e " ${INFO} Requested option \"${1}\" is not available"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Force updating everything
|
||||
if [[ ! "${1}" == "web" && ! "${1}" == "ftl" ]]; then
|
||||
echo -e " ${INFO} Running installer to upgrade your installation"
|
||||
if "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" --unattended; then
|
||||
exit 0
|
||||
else
|
||||
echo -e " ${COL_LIGHT_RED} Error: Unable to complete update, please contact support${COL_NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,38 +8,61 @@
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
if [[ "$@" != *"quiet"* ]]; then
|
||||
echo -n "::: Flushing /var/log/pihole.log ..."
|
||||
colfile="/opt/pihole/COL_TABLE"
|
||||
source ${colfile}
|
||||
|
||||
# Determine database location
|
||||
# Obtain DBFILE=... setting from pihole-FTL.db
|
||||
# Constructed to return nothing when
|
||||
# a) the setting is not present in the config file, or
|
||||
# b) the setting is commented out (e.g. "#DBFILE=...")
|
||||
FTLconf="/etc/pihole/pihole-FTL.conf"
|
||||
if [ -e "$FTLconf" ]; then
|
||||
DBFILE="$(sed -n -e 's/^\s*DBFILE\s*=\s*//p' ${FTLconf})"
|
||||
fi
|
||||
if [[ "$@" == *"once"* ]]; then
|
||||
# Nightly logrotation
|
||||
if command -v /usr/sbin/logrotate >/dev/null; then
|
||||
# Logrotate once
|
||||
/usr/sbin/logrotate --force /etc/pihole/logrotate
|
||||
else
|
||||
# Copy pihole.log over to pihole.log.1
|
||||
# and empty out pihole.log
|
||||
# Note that moving the file is not an option, as
|
||||
# dnsmasq would happily continue writing into the
|
||||
# moved file (it will have the same file handler)
|
||||
cp /var/log/pihole.log /var/log/pihole.log.1
|
||||
echo " " > /var/log/pihole.log
|
||||
fi
|
||||
else
|
||||
# Manual flushing
|
||||
if command -v /usr/sbin/logrotate >/dev/null; then
|
||||
# Logrotate twice to move all data out of sight of FTL
|
||||
/usr/sbin/logrotate --force /etc/pihole/logrotate; sleep 3
|
||||
/usr/sbin/logrotate --force /etc/pihole/logrotate
|
||||
else
|
||||
# Flush both pihole.log and pihole.log.1 (if existing)
|
||||
echo " " > /var/log/pihole.log
|
||||
if [ -f /var/log/pihole.log.1 ]; then
|
||||
echo " " > /var/log/pihole.log.1
|
||||
fi
|
||||
fi
|
||||
# Test for empty string. Use standard path in this case.
|
||||
if [ -z "$DBFILE" ]; then
|
||||
DBFILE="/etc/pihole/pihole-FTL.db"
|
||||
fi
|
||||
|
||||
if [[ "$@" != *"quiet"* ]]; then
|
||||
echo "... done!"
|
||||
echo -ne " ${INFO} Flushing /var/log/pihole.log ..."
|
||||
fi
|
||||
if [[ "$@" == *"once"* ]]; then
|
||||
# Nightly logrotation
|
||||
if command -v /usr/sbin/logrotate >/dev/null; then
|
||||
# Logrotate once
|
||||
/usr/sbin/logrotate --force /etc/pihole/logrotate
|
||||
else
|
||||
# Copy pihole.log over to pihole.log.1
|
||||
# and empty out pihole.log
|
||||
# Note that moving the file is not an option, as
|
||||
# dnsmasq would happily continue writing into the
|
||||
# moved file (it will have the same file handler)
|
||||
cp /var/log/pihole.log /var/log/pihole.log.1
|
||||
echo " " > /var/log/pihole.log
|
||||
fi
|
||||
else
|
||||
# Manual flushing
|
||||
if command -v /usr/sbin/logrotate >/dev/null; then
|
||||
# Logrotate twice to move all data out of sight of FTL
|
||||
/usr/sbin/logrotate --force /etc/pihole/logrotate; sleep 3
|
||||
/usr/sbin/logrotate --force /etc/pihole/logrotate
|
||||
else
|
||||
# Flush both pihole.log and pihole.log.1 (if existing)
|
||||
echo " " > /var/log/pihole.log
|
||||
if [ -f /var/log/pihole.log.1 ]; then
|
||||
echo " " > /var/log/pihole.log.1
|
||||
fi
|
||||
fi
|
||||
# Delete most recent 24 hours from FTL's database, leave even older data intact (don't wipe out all history)
|
||||
deleted=$(sqlite3 "${DBFILE}" "DELETE FROM queries WHERE timestamp >= strftime('%s','now')-86400; select changes() from queries limit 1")
|
||||
|
||||
# Restart pihole-FTL to force reloading history
|
||||
sudo pihole restartdns
|
||||
fi
|
||||
|
||||
if [[ "$@" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Flushed /var/log/pihole.log"
|
||||
echo -e " ${TICK} Deleted ${deleted} queries from database"
|
||||
fi
|
||||
|
239
advanced/Scripts/query.sh
Normal file
239
advanced/Scripts/query.sh
Normal file
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1090
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2018 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Query Domain Lists
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
# Globals
|
||||
piholeDir="/etc/pihole"
|
||||
adListsList="$piholeDir/adlists.list"
|
||||
wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
|
||||
options="$*"
|
||||
adlist=""
|
||||
all=""
|
||||
exact=""
|
||||
blockpage=""
|
||||
matchType="match"
|
||||
|
||||
colfile="/opt/pihole/COL_TABLE"
|
||||
source "${colfile}"
|
||||
|
||||
# Print each subdomain
|
||||
# e.g: foo.bar.baz.com = "foo.bar.baz.com bar.baz.com baz.com com"
|
||||
processWildcards() {
|
||||
IFS="." read -r -a array <<< "${1}"
|
||||
for (( i=${#array[@]}-1; i>=0; i-- )); do
|
||||
ar=""
|
||||
for (( j=${#array[@]}-1; j>${#array[@]}-i-2; j-- )); do
|
||||
if [[ $j == $((${#array[@]}-1)) ]]; then
|
||||
ar="${array[$j]}"
|
||||
else
|
||||
ar="${array[$j]}.${ar}"
|
||||
fi
|
||||
done
|
||||
echo "${ar}"
|
||||
done
|
||||
}
|
||||
|
||||
# Scan an array of files for matching strings
|
||||
scanList(){
|
||||
# Escape full stops
|
||||
local domain="${1//./\\.}" lists="${2}" type="${3:-}"
|
||||
|
||||
# Prevent grep from printing file path
|
||||
cd "$piholeDir" || exit 1
|
||||
|
||||
# Prevent grep -i matching slowly: http://bit.ly/2xFXtUX
|
||||
export LC_CTYPE=C
|
||||
|
||||
# /dev/null forces filename to be printed when only one list has been generated
|
||||
# shellcheck disable=SC2086
|
||||
case "${type}" in
|
||||
"exact" ) grep -i -E -l "(^|\\s)${domain}($|\\s|#)" ${lists} /dev/null 2>/dev/null;;
|
||||
"wc" ) grep -i -o -m 1 "/${domain}/" ${lists} 2>/dev/null;;
|
||||
* ) grep -i "${domain}" ${lists} /dev/null 2>/dev/null;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [[ "${options}" == "-h" ]] || [[ "${options}" == "--help" ]]; then
|
||||
echo "Usage: pihole -q [option] <domain>
|
||||
Example: 'pihole -q -exact domain.com'
|
||||
Query the adlists for a specified domain
|
||||
|
||||
Options:
|
||||
-adlist Print the name of the block list URL
|
||||
-exact Search the block lists for exact domain matches
|
||||
-all Return all query matches within a block list
|
||||
-h, --help Show this help dialog"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -e "$adListsList" ]]; then
|
||||
echo -e "${COL_LIGHT_RED}The file $adListsList was not found${COL_NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Handle valid options
|
||||
if [[ "${options}" == *"-bp"* ]]; then
|
||||
exact="exact"; blockpage=true
|
||||
else
|
||||
[[ "${options}" == *"-adlist"* ]] && adlist=true
|
||||
[[ "${options}" == *"-all"* ]] && all=true
|
||||
if [[ "${options}" == *"-exact"* ]]; then
|
||||
exact="exact"; matchType="exact ${matchType}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Strip valid options, leaving only the domain and invalid options
|
||||
# This allows users to place the options before or after the domain
|
||||
options=$(sed -E 's/ ?-(bp|adlists?|all|exact) ?//g' <<< "${options}")
|
||||
|
||||
# Handle remaining options
|
||||
# If $options contain non ASCII characters, convert to punycode
|
||||
case "${options}" in
|
||||
"" ) str="No domain specified";;
|
||||
*" "* ) str="Unknown query option specified";;
|
||||
*[![:ascii:]]* ) domainQuery=$(idn2 "${options}");;
|
||||
* ) domainQuery="${options}";;
|
||||
esac
|
||||
|
||||
if [[ -n "${str:-}" ]]; then
|
||||
echo -e "${str}${COL_NC}\\nTry 'pihole -q --help' for more information."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Scan Whitelist and Blacklist
|
||||
lists="whitelist.txt blacklist.txt"
|
||||
mapfile -t results <<< "$(scanList "${domainQuery}" "${lists}" "${exact}")"
|
||||
if [[ -n "${results[*]}" ]]; then
|
||||
wbMatch=true
|
||||
# Loop through each result in order to print unique file title once
|
||||
for result in "${results[@]}"; do
|
||||
fileName="${result%%.*}"
|
||||
if [[ -n "${blockpage}" ]]; then
|
||||
echo "π ${result}"
|
||||
exit 0
|
||||
elif [[ -n "${exact}" ]]; then
|
||||
echo " ${matchType^} found in ${COL_BOLD}${fileName^}${COL_NC}"
|
||||
else
|
||||
# Only print filename title once per file
|
||||
if [[ ! "${fileName}" == "${fileName_prev:-}" ]]; then
|
||||
echo " ${matchType^} found in ${COL_BOLD}${fileName^}${COL_NC}"
|
||||
fileName_prev="${fileName}"
|
||||
fi
|
||||
echo " ${result#*:}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Scan Wildcards
|
||||
if [[ -e "${wildcardlist}" ]]; then
|
||||
# Determine all subdomains, domain and TLDs
|
||||
mapfile -t wildcards <<< "$(processWildcards "${domainQuery}")"
|
||||
for match in "${wildcards[@]}"; do
|
||||
# Search wildcard list for matches
|
||||
mapfile -t results <<< "$(scanList "${match}" "${wildcardlist}" "wc")"
|
||||
if [[ -n "${results[*]}" ]]; then
|
||||
if [[ -z "${wcMatch:-}" ]] && [[ -z "${blockpage}" ]]; then
|
||||
wcMatch=true
|
||||
echo " ${matchType^} found in ${COL_BOLD}Wildcards${COL_NC}:"
|
||||
fi
|
||||
case "${blockpage}" in
|
||||
true ) echo "π ${wildcardlist##*/}"; exit 0;;
|
||||
* ) echo " *.${match}";;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Get version sorted *.domains filenames (without dir path)
|
||||
lists=("$(cd "$piholeDir" || exit 0; printf "%s\\n" -- *.domains | sort -V)")
|
||||
|
||||
# Query blocklists for occurences of domain
|
||||
mapfile -t results <<< "$(scanList "${domainQuery}" "${lists[*]}" "${exact}")"
|
||||
|
||||
# Handle notices
|
||||
if [[ -z "${wbMatch:-}" ]] && [[ -z "${wcMatch:-}" ]] && [[ -z "${results[*]}" ]]; then
|
||||
echo -e " ${INFO} No ${exact/t/t }results found for ${COL_BOLD}${domainQuery}${COL_NC} within the block lists"
|
||||
exit 0
|
||||
elif [[ -z "${results[*]}" ]]; then
|
||||
# Result found in WL/BL/Wildcards
|
||||
exit 0
|
||||
elif [[ -z "${all}" ]] && [[ "${#results[*]}" -ge 100 ]]; then
|
||||
echo -e " ${INFO} Over 100 ${exact/t/t }results found for ${COL_BOLD}${domainQuery}${COL_NC}
|
||||
This can be overridden using the -all option"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Remove unwanted content from non-exact $results
|
||||
if [[ -z "${exact}" ]]; then
|
||||
# Delete lines starting with #
|
||||
# Remove comments after domain
|
||||
# Remove hosts format IP address
|
||||
mapfile -t results <<< "$(IFS=$'\n'; sed \
|
||||
-e "/:#/d" \
|
||||
-e "s/[ \\t]#.*//g" \
|
||||
-e "s/:.*[ \\t]/:/g" \
|
||||
<<< "${results[*]}")"
|
||||
# Exit if result was in a comment
|
||||
[[ -z "${results[*]}" ]] && exit 0
|
||||
fi
|
||||
|
||||
# Get adlist file content as array
|
||||
if [[ -n "${adlist}" ]] || [[ -n "${blockpage}" ]]; then
|
||||
for adlistUrl in $(< "${adListsList}"); do
|
||||
if [[ "${adlistUrl:0:4}" =~ (http|www.) ]]; then
|
||||
adlists+=("${adlistUrl}")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Print "Exact matches for" title
|
||||
if [[ -n "${exact}" ]] && [[ -z "${blockpage}" ]]; then
|
||||
plural=""; [[ "${#results[*]}" -gt 1 ]] && plural="es"
|
||||
echo " ${matchType^}${plural} for ${COL_BOLD}${domainQuery}${COL_NC} found in:"
|
||||
fi
|
||||
|
||||
for result in "${results[@]}"; do
|
||||
fileName="${result/:*/}"
|
||||
|
||||
# Determine *.domains URL using filename's number
|
||||
if [[ -n "${adlist}" ]] || [[ -n "${blockpage}" ]]; then
|
||||
fileNum="${fileName/list./}"; fileNum="${fileNum%%.*}"
|
||||
fileName="${adlists[$fileNum]}"
|
||||
|
||||
# Discrepency occurs when adlists has been modified, but Gravity has not been run
|
||||
if [[ -z "${fileName}" ]]; then
|
||||
fileName="${COL_LIGHT_RED}(no associated adlists URL found)${COL_NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${blockpage}" ]]; then
|
||||
echo "${fileNum} ${fileName}"
|
||||
elif [[ -n "${exact}" ]]; then
|
||||
echo " ${fileName}"
|
||||
else
|
||||
if [[ ! "${fileName}" == "${fileName_prev:-}" ]]; then
|
||||
count=""
|
||||
echo " ${matchType^} found in ${COL_BOLD}${fileName}${COL_NC}:"
|
||||
fileName_prev="${fileName}"
|
||||
fi
|
||||
: $((count++))
|
||||
|
||||
# Print matching domain if $max_count has not been reached
|
||||
[[ -z "${all}" ]] && max_count="50"
|
||||
if [[ -z "${all}" ]] && [[ "${count}" -ge "${max_count}" ]]; then
|
||||
[[ "${count}" -gt "${max_count}" ]] && continue
|
||||
echo " ${COL_GRAY}Over ${count} results found, skipping rest of file${COL_NC}"
|
||||
else
|
||||
echo " ${result#*:}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
@@ -15,28 +15,28 @@
|
||||
# Borrowed from adafruit-pitft-helper < borrowed from raspi-config
|
||||
# https://github.com/adafruit/Adafruit-PiTFT-Helper/blob/master/adafruit-pitft-helper#L324-L334
|
||||
getInitSys() {
|
||||
if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then
|
||||
SYSTEMD=1
|
||||
elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
|
||||
SYSTEMD=0
|
||||
else
|
||||
echo "Unrecognised init system"
|
||||
return 1
|
||||
fi
|
||||
if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then
|
||||
SYSTEMD=1
|
||||
elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
|
||||
SYSTEMD=0
|
||||
else
|
||||
echo "Unrecognised init system"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Borrowed from adafruit-pitft-helper:
|
||||
# https://github.com/adafruit/Adafruit-PiTFT-Helper/blob/master/adafruit-pitft-helper#L274-L285
|
||||
autoLoginPiToConsole() {
|
||||
if [ -e /etc/init.d/lightdm ]; then
|
||||
if [ ${SYSTEMD} -eq 1 ]; then
|
||||
systemctl set-default multi-user.target
|
||||
ln -fs /etc/systemd/system/autologin@.service /etc/systemd/system/getty.target.wants/getty@tty1.service
|
||||
else
|
||||
update-rc.d lightdm disable 2
|
||||
sed /etc/inittab -i -e "s/1:2345:respawn:\/sbin\/getty --noclear 38400 tty1/1:2345:respawn:\/bin\/login -f pi tty1 <\/dev\/tty1 >\/dev\/tty1 2>&1/"
|
||||
fi
|
||||
fi
|
||||
if [ -e /etc/init.d/lightdm ]; then
|
||||
if [ ${SYSTEMD} -eq 1 ]; then
|
||||
systemctl set-default multi-user.target
|
||||
ln -fs /etc/systemd/system/autologin@.service /etc/systemd/system/getty.target.wants/getty@tty1.service
|
||||
else
|
||||
update-rc.d lightdm disable 2
|
||||
sed /etc/inittab -i -e "s/1:2345:respawn:\/sbin\/getty --noclear 38400 tty1/1:2345:respawn:\/bin\/login -f pi tty1 <\/dev\/tty1 >\/dev\/tty1 2>&1/"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
######### SCRIPT ###########
|
||||
|
@@ -10,216 +10,182 @@
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
|
||||
|
||||
# Variables
|
||||
|
||||
readonly ADMIN_INTERFACE_GIT_URL="https://github.com/pi-hole/AdminLTE.git"
|
||||
readonly ADMIN_INTERFACE_DIR="/var/www/html/admin"
|
||||
readonly PI_HOLE_GIT_URL="https://github.com/pi-hole/pi-hole.git"
|
||||
readonly PI_HOLE_FILES_DIR="/etc/.pihole"
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
PH_TEST=true
|
||||
source ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh
|
||||
|
||||
# when --check-only is passed to this script, it will not perform the actual update
|
||||
CHECK_ONLY=false
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
|
||||
# shellcheck disable=SC1091
|
||||
source "/opt/pihole/COL_TABLE"
|
||||
|
||||
# is_repo() sourced from basic-install.sh
|
||||
# make_repo() sourced from basic-install.sh
|
||||
# update_repo() source from basic-install.sh
|
||||
# getGitFiles() sourced from basic-install.sh
|
||||
# get_binary_name() sourced from basic-install.sh
|
||||
# FTLcheckUpdate() sourced from basic-install.sh
|
||||
|
||||
GitCheckUpdateAvail() {
|
||||
local directory="${1}"
|
||||
curdir=$PWD
|
||||
cd "${directory}"
|
||||
local directory
|
||||
directory="${1}"
|
||||
curdir=$PWD
|
||||
cd "${directory}" || return
|
||||
|
||||
# Fetch latest changes in this repo
|
||||
git fetch --quiet origin
|
||||
# Fetch latest changes in this repo
|
||||
git fetch --quiet origin
|
||||
|
||||
# @ alone is a shortcut for HEAD. Older versions of git
|
||||
# need @{0}
|
||||
LOCAL="$(git rev-parse @{0})"
|
||||
# @ alone is a shortcut for HEAD. Older versions of git
|
||||
# need @{0}
|
||||
LOCAL="$(git rev-parse "@{0}")"
|
||||
|
||||
# The suffix @{upstream} to a branchname
|
||||
# (short form <branchname>@{u}) refers
|
||||
# to the branch that the branch specified
|
||||
# by branchname is set to build on top of#
|
||||
# (configured with branch.<name>.remote and
|
||||
# branch.<name>.merge). A missing branchname
|
||||
# defaults to the current one.
|
||||
REMOTE="$(git rev-parse @{upstream})"
|
||||
# The suffix @{upstream} to a branchname
|
||||
# (short form <branchname>@{u}) refers
|
||||
# to the branch that the branch specified
|
||||
# by branchname is set to build on top of#
|
||||
# (configured with branch.<name>.remote and
|
||||
# branch.<name>.merge). A missing branchname
|
||||
# defaults to the current one.
|
||||
REMOTE="$(git rev-parse "@{upstream}")"
|
||||
|
||||
if [[ ${#LOCAL} == 0 ]]; then
|
||||
echo "::: Error: Local revision could not be obtained, ask Pi-hole support."
|
||||
echo "::: Additional debugging output:"
|
||||
git status
|
||||
exit
|
||||
fi
|
||||
if [[ ${#REMOTE} == 0 ]]; then
|
||||
echo "::: Error: Remote revision could not be obtained, ask Pi-hole support."
|
||||
echo "::: Additional debugging output:"
|
||||
git status
|
||||
exit
|
||||
fi
|
||||
if [[ "${#LOCAL}" == 0 ]]; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Local revision could not be obtained, please contact Pi-hole Support"
|
||||
echo -e " Additional debugging output:${COL_NC}"
|
||||
git status
|
||||
exit
|
||||
fi
|
||||
if [[ "${#REMOTE}" == 0 ]]; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Remote revision could not be obtained, please contact Pi-hole Support"
|
||||
echo -e " Additional debugging output:${COL_NC}"
|
||||
git status
|
||||
exit
|
||||
fi
|
||||
|
||||
# Change back to original directory
|
||||
cd "${curdir}"
|
||||
# Change back to original directory
|
||||
cd "${curdir}" || exit
|
||||
|
||||
if [[ "${LOCAL}" != "${REMOTE}" ]]; then
|
||||
# Local branch is behind remote branch -> Update
|
||||
return 0
|
||||
else
|
||||
# Local branch is up-to-date or in a situation
|
||||
# where this updater cannot be used (like on a
|
||||
# branch that exists only locally)
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
FTLcheckUpdate() {
|
||||
|
||||
local FTLversion=$(/usr/bin/pihole-FTL tag)
|
||||
local FTLlatesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep 'Location' | awk -F '/' '{print $NF}' | tr -d '\r\n')
|
||||
|
||||
if [[ "${FTLversion}" != "${FTLlatesttag}" ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
if [[ "${LOCAL}" != "${REMOTE}" ]]; then
|
||||
# Local branch is behind remote branch -> Update
|
||||
return 0
|
||||
else
|
||||
# Local branch is up-to-date or in a situation
|
||||
# where this updater cannot be used (like on a
|
||||
# branch that exists only locally)
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local pihole_version_current
|
||||
local web_version_current
|
||||
source "${setupVars}"
|
||||
local basicError="\\n ${COL_LIGHT_RED}Unable to complete update, please contact Pi-hole Support${COL_NC}"
|
||||
local core_update
|
||||
local web_update
|
||||
local FTL_update
|
||||
|
||||
#This is unlikely
|
||||
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
|
||||
echo "::: Critical Error: Core Pi-hole repo is missing from system!"
|
||||
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
echo "::: Checking for updates..."
|
||||
|
||||
if GitCheckUpdateAvail "${PI_HOLE_FILES_DIR}" ; then
|
||||
core_update=true
|
||||
echo "::: Pi-hole Core: update available"
|
||||
else
|
||||
core_update=false
|
||||
echo "::: Pi-hole Core: up to date"
|
||||
fi
|
||||
|
||||
if FTLcheckUpdate ; then
|
||||
FTL_update=true
|
||||
echo "::: FTL: update available"
|
||||
else
|
||||
web_update=false
|
||||
FTL_update=false
|
||||
echo "::: FTL: up to date"
|
||||
fi
|
||||
|
||||
# Logic: Don't update FTL when there is a core update available
|
||||
# since the core update will run the installer which will itself
|
||||
# re-install (i.e. update) FTL
|
||||
if ${FTL_update} && ! ${core_update}; then
|
||||
echo ":::"
|
||||
echo "::: FTL out of date"
|
||||
FTLdetect
|
||||
echo ":::"
|
||||
fi
|
||||
# shellcheck disable=1090,2154
|
||||
source "${setupVars}"
|
||||
|
||||
if [[ ${INSTALL_WEB} == true ]]; then
|
||||
if ! is_repo "${ADMIN_INTERFACE_DIR}" ; then
|
||||
echo "::: Critical Error: Web Admin repo is missing from system!"
|
||||
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole"
|
||||
exit 1;
|
||||
# This is unlikely
|
||||
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!"
|
||||
echo -e " Please re-run install script from https://pi-hole.net${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if GitCheckUpdateAvail "${ADMIN_INTERFACE_DIR}" ; then
|
||||
web_update=true
|
||||
echo "::: Web Interface: update available"
|
||||
echo -e " ${INFO} Checking for updates..."
|
||||
|
||||
if GitCheckUpdateAvail "${PI_HOLE_FILES_DIR}" ; then
|
||||
core_update=true
|
||||
echo -e " ${INFO} Pi-hole Core:\\t${COL_YELLOW}update available${COL_NC}"
|
||||
else
|
||||
web_update=false
|
||||
echo "::: Web Interface: up to date"
|
||||
core_update=false
|
||||
echo -e " ${INFO} Pi-hole Core:\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
|
||||
fi
|
||||
|
||||
# Logic
|
||||
# If Core up to date AND web up to date:
|
||||
# Do nothing
|
||||
# If Core up to date AND web NOT up to date:
|
||||
# Pull web repo
|
||||
# If Core NOT up to date AND web up to date:
|
||||
# pull pihole repo, run install --unattended -- reconfigure
|
||||
# if Core NOT up to date AND web NOT up to date:
|
||||
# pull pihole repo run install --unattended
|
||||
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
|
||||
if ! is_repo "${ADMIN_INTERFACE_DIR}" ; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!"
|
||||
echo -e " Please re-run install script from https://pi-hole.net${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if ! ${core_update} && ! ${web_update} ; then
|
||||
if ! ${FTL_update} ; then
|
||||
echo ":::"
|
||||
echo "::: Everything is up to date!"
|
||||
if GitCheckUpdateAvail "${ADMIN_INTERFACE_DIR}" ; then
|
||||
web_update=true
|
||||
echo -e " ${INFO} Web Interface:\\t${COL_YELLOW}update available${COL_NC}"
|
||||
else
|
||||
web_update=false
|
||||
echo -e " ${INFO} Web Interface:\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if FTLcheckUpdate > /dev/null; then
|
||||
FTL_update=true
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_YELLOW}update available${COL_NC}"
|
||||
else
|
||||
case $? in
|
||||
1)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
|
||||
;;
|
||||
2)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_RED}Branch is not available.${COL_NC}\\n\\t\\t\\tUse ${COL_LIGHT_GREEN}pihole checkout ftl [branchname]${COL_NC} to switch to a valid branch."
|
||||
;;
|
||||
*)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_RED}Something has gone wrong, contact support${COL_NC}"
|
||||
esac
|
||||
FTL_update=false
|
||||
fi
|
||||
|
||||
if [[ "${core_update}" == false && "${web_update}" == false && "${FTL_update}" == false ]]; then
|
||||
echo ""
|
||||
echo -e " ${TICK} Everything is up to date!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
elif ! ${core_update} && ${web_update} ; then
|
||||
echo ":::"
|
||||
echo "::: Pi-hole Web Admin files out of date"
|
||||
getGitFiles "${ADMIN_INTERFACE_DIR}" "${ADMIN_INTERFACE_GIT_URL}"
|
||||
|
||||
elif ${core_update} && ! ${web_update} ; then
|
||||
echo ":::"
|
||||
echo "::: Pi-hole core files out of date"
|
||||
getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}"
|
||||
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1
|
||||
|
||||
elif ${core_update} && ${web_update} ; then
|
||||
echo ":::"
|
||||
echo "::: Updating Pi-hole core and web admin files"
|
||||
getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}"
|
||||
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1
|
||||
else
|
||||
echo "*** Update script has malfunctioned, fallthrough reached. Please contact support"
|
||||
exit 1
|
||||
fi
|
||||
else # Web Admin not installed, so only verify if core is up to date
|
||||
if ! ${core_update}; then
|
||||
if ! ${FTL_update} ; then
|
||||
echo ":::"
|
||||
echo "::: Everything is up to date!"
|
||||
|
||||
if [[ "${CHECK_ONLY}" == true ]]; then
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo ":::"
|
||||
echo "::: Pi-hole core files out of date"
|
||||
getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}"
|
||||
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${web_update}" == true ]]; then
|
||||
web_version_current="$(/usr/local/bin/pihole version --admin --current)"
|
||||
echo ":::"
|
||||
echo "::: Web Admin version is now at ${web_version_current/* v/v}}"
|
||||
echo "::: If you had made any changes in '/var/www/html/admin/', they have been stashed using 'git stash'"
|
||||
fi
|
||||
if [[ "${core_update}" == true ]]; then
|
||||
echo ""
|
||||
echo -e " ${INFO} Pi-hole core files out of date, updating local repo."
|
||||
getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}"
|
||||
echo -e " ${INFO} If you had made any changes in '/etc/.pihole/', they have been stashed using 'git stash'"
|
||||
fi
|
||||
|
||||
if [[ "${core_update}" == true ]]; then
|
||||
pihole_version_current="$(/usr/local/bin/pihole version --pihole --current)"
|
||||
echo ":::"
|
||||
echo "::: Pi-hole version is now at ${pihole_version_current/* v/v}}"
|
||||
echo "::: If you had made any changes in '/etc/.pihole/', they have been stashed using 'git stash'"
|
||||
fi
|
||||
if [[ "${web_update}" == true ]]; then
|
||||
echo ""
|
||||
echo -e " ${INFO} Pi-hole Web Admin files out of date, updating local repo."
|
||||
getGitFiles "${ADMIN_INTERFACE_DIR}" "${ADMIN_INTERFACE_GIT_URL}"
|
||||
echo -e " ${INFO} If you had made any changes in '/var/www/html/admin/', they have been stashed using 'git stash'"
|
||||
fi
|
||||
|
||||
if [[ ${FTL_update} == true ]]; then
|
||||
FTL_version_current="$(/usr/local/bin/pihole version --ftl --current)"
|
||||
echo ":::"
|
||||
echo "::: FTL version is now at ${FTL_version_current/* v/v}}"
|
||||
start_service pihole-FTL
|
||||
enable_service pihole-FTL
|
||||
fi
|
||||
|
||||
|
||||
echo ""
|
||||
exit 0
|
||||
if [[ "${FTL_update}" == true ]]; then
|
||||
echo ""
|
||||
echo -e " ${INFO} FTL out of date, it will be updated by the installer."
|
||||
fi
|
||||
|
||||
if [[ "${FTL_update}" == true || "${core_update}" == true ]]; then
|
||||
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \
|
||||
echo -e "${basicError}" && exit 1
|
||||
fi
|
||||
echo ""
|
||||
exit 0
|
||||
}
|
||||
|
||||
if [[ "$1" == "--check-only" ]]; then
|
||||
CHECK_ONLY=true
|
||||
fi
|
||||
|
||||
main
|
||||
|
91
advanced/Scripts/updatecheck.sh
Executable file
91
advanced/Scripts/updatecheck.sh
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Checks for local or remote versions and branches
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
# Credit: https://stackoverflow.com/a/46324904
|
||||
function json_extract() {
|
||||
local key=$1
|
||||
local json=$2
|
||||
|
||||
local string_regex='"([^"\]|\\.)*"'
|
||||
local number_regex='-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?'
|
||||
local value_regex="${string_regex}|${number_regex}|true|false|null"
|
||||
local pair_regex="\"${key}\"[[:space:]]*:[[:space:]]*(${value_regex})"
|
||||
|
||||
if [[ ${json} =~ ${pair_regex} ]]; then
|
||||
echo $(sed 's/^"\|"$//g' <<< "${BASH_REMATCH[1]}")
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function get_local_branch() {
|
||||
# Return active branch
|
||||
cd "${1}" 2> /dev/null || return 1
|
||||
git rev-parse --abbrev-ref HEAD || return 1
|
||||
}
|
||||
|
||||
function get_local_version() {
|
||||
# Return active branch
|
||||
cd "${1}" 2> /dev/null || return 1
|
||||
git describe --long --dirty --tags || return 1
|
||||
}
|
||||
|
||||
# Source the setupvars config file
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/pihole/setupVars.conf
|
||||
|
||||
if [[ "$2" == "remote" ]]; then
|
||||
|
||||
if [[ "$3" == "reboot" ]]; then
|
||||
sleep 30
|
||||
fi
|
||||
|
||||
GITHUB_VERSION_FILE="/etc/pihole/GitHubVersions"
|
||||
|
||||
GITHUB_CORE_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/pi-hole/releases/latest' 2> /dev/null)")"
|
||||
echo -n "${GITHUB_CORE_VERSION}" > "${GITHUB_VERSION_FILE}"
|
||||
|
||||
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
|
||||
GITHUB_WEB_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/AdminLTE/releases/latest' 2> /dev/null)")"
|
||||
echo -n " ${GITHUB_WEB_VERSION}" >> "${GITHUB_VERSION_FILE}"
|
||||
fi
|
||||
|
||||
GITHUB_FTL_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/FTL/releases/latest' 2> /dev/null)")"
|
||||
echo -n " ${GITHUB_FTL_VERSION}" >> "${GITHUB_VERSION_FILE}"
|
||||
|
||||
else
|
||||
|
||||
LOCAL_BRANCH_FILE="/etc/pihole/localbranches"
|
||||
|
||||
CORE_BRANCH="$(get_local_branch /etc/.pihole)"
|
||||
echo -n "${CORE_BRANCH}" > "${LOCAL_BRANCH_FILE}"
|
||||
|
||||
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
|
||||
WEB_BRANCH="$(get_local_branch /var/www/html/admin)"
|
||||
echo -n " ${WEB_BRANCH}" >> "${LOCAL_BRANCH_FILE}"
|
||||
fi
|
||||
|
||||
FTL_BRANCH="$(pihole-FTL branch)"
|
||||
echo -n " ${FTL_BRANCH}" >> "${LOCAL_BRANCH_FILE}"
|
||||
|
||||
LOCAL_VERSION_FILE="/etc/pihole/localversions"
|
||||
|
||||
CORE_VERSION="$(get_local_version /etc/.pihole)"
|
||||
echo -n "${CORE_VERSION}" > "${LOCAL_VERSION_FILE}"
|
||||
|
||||
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
|
||||
WEB_VERSION="$(get_local_version /var/www/html/admin)"
|
||||
echo -n " ${WEB_VERSION}" >> "${LOCAL_VERSION_FILE}"
|
||||
fi
|
||||
|
||||
FTL_VERSION="$(pihole-FTL version)"
|
||||
echo -n " ${FTL_VERSION}" >> "${LOCAL_VERSION_FILE}"
|
||||
|
||||
fi
|
@@ -14,135 +14,135 @@ COREGITDIR="/etc/.pihole/"
|
||||
WEBGITDIR="/var/www/html/admin/"
|
||||
|
||||
getLocalVersion() {
|
||||
# FTL requires a different method
|
||||
if [[ "$1" == "FTL" ]]; then
|
||||
pihole-FTL version
|
||||
# FTL requires a different method
|
||||
if [[ "$1" == "FTL" ]]; then
|
||||
pihole-FTL version
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Get the tagged version of the local repository
|
||||
local directory="${1}"
|
||||
local version
|
||||
|
||||
cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; }
|
||||
version=$(git describe --tags --always || echo "$DEFAULT")
|
||||
if [[ "${version}" =~ ^v ]]; then
|
||||
echo "${version}"
|
||||
elif [[ "${version}" == "${DEFAULT}" ]]; then
|
||||
echo "ERROR"
|
||||
return 1
|
||||
else
|
||||
echo "Untagged"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Get the tagged version of the local repository
|
||||
local directory="${1}"
|
||||
local version
|
||||
|
||||
cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; }
|
||||
version=$(git describe --tags --always || echo "$DEFAULT")
|
||||
if [[ "${version}" =~ ^v ]]; then
|
||||
echo "${version}"
|
||||
elif [[ "${version}" == "${DEFAULT}" ]]; then
|
||||
echo "ERROR"
|
||||
return 1
|
||||
else
|
||||
echo "Untagged"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
getLocalHash() {
|
||||
# Local FTL hash does not exist on filesystem
|
||||
if [[ "$1" == "FTL" ]]; then
|
||||
echo "N/A"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Get the short hash of the local repository
|
||||
local directory="${1}"
|
||||
local hash
|
||||
# Local FTL hash does not exist on filesystem
|
||||
if [[ "$1" == "FTL" ]]; then
|
||||
echo "N/A"
|
||||
return 0
|
||||
fi
|
||||
|
||||
cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; }
|
||||
hash=$(git rev-parse --short HEAD || echo "$DEFAULT")
|
||||
if [[ "${hash}" == "${DEFAULT}" ]]; then
|
||||
echo "ERROR"
|
||||
return 1
|
||||
else
|
||||
echo "${hash}"
|
||||
fi
|
||||
return 0
|
||||
# Get the short hash of the local repository
|
||||
local directory="${1}"
|
||||
local hash
|
||||
|
||||
cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; }
|
||||
hash=$(git rev-parse --short HEAD || echo "$DEFAULT")
|
||||
if [[ "${hash}" == "${DEFAULT}" ]]; then
|
||||
echo "ERROR"
|
||||
return 1
|
||||
else
|
||||
echo "${hash}"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
getRemoteHash(){
|
||||
# Remote FTL hash is not applicable
|
||||
if [[ "$1" == "FTL" ]]; then
|
||||
echo "N/A"
|
||||
# Remote FTL hash is not applicable
|
||||
if [[ "$1" == "FTL" ]]; then
|
||||
echo "N/A"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local daemon="${1}"
|
||||
local branch="${2}"
|
||||
|
||||
hash=$(git ls-remote --heads "https://github.com/pi-hole/${daemon}" | \
|
||||
awk -v bra="$branch" '$0~bra {print substr($0,0,8);exit}')
|
||||
if [[ -n "$hash" ]]; then
|
||||
echo "$hash"
|
||||
else
|
||||
echo "ERROR"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
local daemon="${1}"
|
||||
local branch="${2}"
|
||||
|
||||
hash=$(git ls-remote --heads "https://github.com/pi-hole/${daemon}" | \
|
||||
awk -v bra="$branch" '$0~bra {print substr($0,0,8);exit}')
|
||||
if [[ -n "$hash" ]]; then
|
||||
echo "$hash"
|
||||
else
|
||||
echo "ERROR"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
getRemoteVersion(){
|
||||
# Get the version from the remote origin
|
||||
local daemon="${1}"
|
||||
local version
|
||||
# Get the version from the remote origin
|
||||
local daemon="${1}"
|
||||
local version
|
||||
|
||||
version=$(curl --silent --fail "https://api.github.com/repos/pi-hole/${daemon}/releases/latest" | \
|
||||
awk -F: '$1 ~/tag_name/ { print $2 }' | \
|
||||
tr -cd '[[:alnum:]]._-')
|
||||
if [[ "${version}" =~ ^v ]]; then
|
||||
echo "${version}"
|
||||
else
|
||||
echo "ERROR"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
version=$(curl --silent --fail "https://api.github.com/repos/pi-hole/${daemon}/releases/latest" | \
|
||||
awk -F: '$1 ~/tag_name/ { print $2 }' | \
|
||||
tr -cd '[[:alnum:]]._-')
|
||||
if [[ "${version}" =~ ^v ]]; then
|
||||
echo "${version}"
|
||||
else
|
||||
echo "ERROR"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
versionOutput() {
|
||||
[[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR
|
||||
[[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR
|
||||
[[ "$1" == "FTL" ]] && GITDIR="FTL"
|
||||
|
||||
[[ "$2" == "-c" ]] || [[ "$2" == "--current" ]] || [[ -z "$2" ]] && current=$(getLocalVersion $GITDIR)
|
||||
[[ "$2" == "-l" ]] || [[ "$2" == "--latest" ]] || [[ -z "$2" ]] && latest=$(getRemoteVersion "$1")
|
||||
if [[ "$2" == "-h" ]] || [[ "$2" == "--hash" ]]; then
|
||||
[[ "$3" == "-c" ]] || [[ "$3" == "--current" ]] || [[ -z "$3" ]] && curHash=$(getLocalHash "$GITDIR")
|
||||
[[ "$3" == "-l" ]] || [[ "$3" == "--latest" ]] || [[ -z "$3" ]] && latHash=$(getRemoteHash "$1" "$(cd "$GITDIR" 2> /dev/null && git rev-parse --abbrev-ref HEAD)")
|
||||
fi
|
||||
[[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR
|
||||
[[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR
|
||||
[[ "$1" == "FTL" ]] && GITDIR="FTL"
|
||||
|
||||
if [[ -n "$current" ]] && [[ -n "$latest" ]]; then
|
||||
output="${1^} version is $current (Latest: $latest)"
|
||||
elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then
|
||||
output="Current ${1^} version is $current"
|
||||
elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then
|
||||
output="Latest ${1^} version is $latest"
|
||||
elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then
|
||||
output="${1^} hash is not applicable"
|
||||
elif [[ -n "$curHash" ]] && [[ -n "$latHash" ]]; then
|
||||
output="${1^} hash is $curHash (Latest: $latHash)"
|
||||
elif [[ -n "$curHash" ]] && [[ -z "$latHash" ]]; then
|
||||
output="Current ${1^} hash is $curHash"
|
||||
elif [[ -z "$curHash" ]] && [[ -n "$latHash" ]]; then
|
||||
output="Latest ${1^} hash is $latHash"
|
||||
else
|
||||
errorOutput
|
||||
fi
|
||||
[[ "$2" == "-c" ]] || [[ "$2" == "--current" ]] || [[ -z "$2" ]] && current=$(getLocalVersion $GITDIR)
|
||||
[[ "$2" == "-l" ]] || [[ "$2" == "--latest" ]] || [[ -z "$2" ]] && latest=$(getRemoteVersion "$1")
|
||||
if [[ "$2" == "-h" ]] || [[ "$2" == "--hash" ]]; then
|
||||
[[ "$3" == "-c" ]] || [[ "$3" == "--current" ]] || [[ -z "$3" ]] && curHash=$(getLocalHash "$GITDIR")
|
||||
[[ "$3" == "-l" ]] || [[ "$3" == "--latest" ]] || [[ -z "$3" ]] && latHash=$(getRemoteHash "$1" "$(cd "$GITDIR" 2> /dev/null && git rev-parse --abbrev-ref HEAD)")
|
||||
fi
|
||||
|
||||
[[ -n "$output" ]] && echo " $output"
|
||||
if [[ -n "$current" ]] && [[ -n "$latest" ]]; then
|
||||
output="${1^} version is $current (Latest: $latest)"
|
||||
elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then
|
||||
output="Current ${1^} version is $current"
|
||||
elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then
|
||||
output="Latest ${1^} version is $latest"
|
||||
elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then
|
||||
output="${1^} hash is not applicable"
|
||||
elif [[ -n "$curHash" ]] && [[ -n "$latHash" ]]; then
|
||||
output="${1^} hash is $curHash (Latest: $latHash)"
|
||||
elif [[ -n "$curHash" ]] && [[ -z "$latHash" ]]; then
|
||||
output="Current ${1^} hash is $curHash"
|
||||
elif [[ -z "$curHash" ]] && [[ -n "$latHash" ]]; then
|
||||
output="Latest ${1^} hash is $latHash"
|
||||
else
|
||||
errorOutput
|
||||
fi
|
||||
|
||||
[[ -n "$output" ]] && echo " $output"
|
||||
}
|
||||
|
||||
errorOutput() {
|
||||
echo " Invalid Option! Try 'pihole -v --help' for more information."
|
||||
exit 1
|
||||
echo " Invalid Option! Try 'pihole -v --help' for more information."
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
defaultOutput() {
|
||||
versionOutput "pi-hole" "$@"
|
||||
versionOutput "AdminLTE" "$@"
|
||||
versionOutput "FTL" "$@"
|
||||
versionOutput "pi-hole" "$@"
|
||||
versionOutput "AdminLTE" "$@"
|
||||
versionOutput "FTL" "$@"
|
||||
}
|
||||
|
||||
helpFunc() {
|
||||
echo "Usage: pihole -v [repo | option] [option]
|
||||
echo "Usage: pihole -v [repo | option] [option]
|
||||
Example: 'pihole -v -p -l'
|
||||
Show Pi-hole, Admin Console & FTL versions
|
||||
|
||||
@@ -150,7 +150,7 @@ Repositories:
|
||||
-p, --pihole Only retrieve info regarding Pi-hole repository
|
||||
-a, --admin Only retrieve info regarding AdminLTE repository
|
||||
-f, --ftl Only retrieve info regarding FTL repository
|
||||
|
||||
|
||||
Options:
|
||||
-c, --current Return the current version
|
||||
-l, --latest Return the latest version
|
||||
@@ -160,9 +160,9 @@ Options:
|
||||
}
|
||||
|
||||
case "${1}" in
|
||||
"-p" | "--pihole" ) shift; versionOutput "pi-hole" "$@";;
|
||||
"-a" | "--admin" ) shift; versionOutput "AdminLTE" "$@";;
|
||||
"-f" | "--ftl" ) shift; versionOutput "FTL" "$@";;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
* ) defaultOutput "$@";;
|
||||
"-p" | "--pihole" ) shift; versionOutput "pi-hole" "$@";;
|
||||
"-a" | "--admin" ) shift; versionOutput "AdminLTE" "$@";;
|
||||
"-f" | "--ftl" ) shift; versionOutput "FTL" "$@";;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
* ) defaultOutput "$@";;
|
||||
esac
|
||||
|
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1090
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
@@ -11,242 +13,302 @@
|
||||
readonly setupVars="/etc/pihole/setupVars.conf"
|
||||
readonly dnsmasqconfig="/etc/dnsmasq.d/01-pihole.conf"
|
||||
readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf"
|
||||
readonly FTLconf="/etc/pihole/pihole-FTL.conf"
|
||||
# 03 -> wildcards
|
||||
readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf"
|
||||
|
||||
coltable="/opt/pihole/COL_TABLE"
|
||||
if [[ -f ${coltable} ]]; then
|
||||
source ${coltable}
|
||||
fi
|
||||
|
||||
helpFunc() {
|
||||
echo "Usage: pihole -a [options]
|
||||
echo "Usage: pihole -a [options]
|
||||
Example: pihole -a -p password
|
||||
Set options for the Admin Console
|
||||
|
||||
Options:
|
||||
-f, flush Flush the Pi-hole log
|
||||
-p, password Set Admin Console password
|
||||
-c, celsius Set Celsius as preferred temperature unit
|
||||
-f, fahrenheit Set Fahrenheit as preferred temperature unit
|
||||
-k, kelvin Set Kelvin as preferred temperature unit
|
||||
-r, hostrecord Add a name to the DNS associated to an IPv4/IPv6 address
|
||||
-e, email Set an administrative contact address for the Block Page
|
||||
-h, --help Show this help dialog
|
||||
-i, interface Specify dnsmasq's interface listening behavior
|
||||
Add '-h' for more info on interface usage"
|
||||
exit 0
|
||||
-l, privacylevel Set privacy level (0 = lowest, 3 = highest)"
|
||||
exit 0
|
||||
}
|
||||
|
||||
add_setting() {
|
||||
echo "${1}=${2}" >> "${setupVars}"
|
||||
echo "${1}=${2}" >> "${setupVars}"
|
||||
}
|
||||
|
||||
delete_setting() {
|
||||
sed -i "/${1}/d" "${setupVars}"
|
||||
sed -i "/${1}/d" "${setupVars}"
|
||||
}
|
||||
|
||||
change_setting() {
|
||||
delete_setting "${1}"
|
||||
add_setting "${1}" "${2}"
|
||||
delete_setting "${1}"
|
||||
add_setting "${1}" "${2}"
|
||||
}
|
||||
|
||||
addFTLsetting() {
|
||||
echo "${1}=${2}" >> "${FTLconf}"
|
||||
}
|
||||
|
||||
deleteFTLsetting() {
|
||||
sed -i "/${1}/d" "${FTLconf}"
|
||||
}
|
||||
|
||||
changeFTLsetting() {
|
||||
deleteFTLsetting "${1}"
|
||||
addFTLsetting "${1}" "${2}"
|
||||
}
|
||||
|
||||
add_dnsmasq_setting() {
|
||||
if [[ "${2}" != "" ]]; then
|
||||
echo "${1}=${2}" >> "${dnsmasqconfig}"
|
||||
else
|
||||
echo "${1}" >> "${dnsmasqconfig}"
|
||||
fi
|
||||
if [[ "${2}" != "" ]]; then
|
||||
echo "${1}=${2}" >> "${dnsmasqconfig}"
|
||||
else
|
||||
echo "${1}" >> "${dnsmasqconfig}"
|
||||
fi
|
||||
}
|
||||
|
||||
delete_dnsmasq_setting() {
|
||||
sed -i "/${1}/d" "${dnsmasqconfig}"
|
||||
sed -i "/${1}/d" "${dnsmasqconfig}"
|
||||
}
|
||||
|
||||
SetTemperatureUnit() {
|
||||
change_setting "TEMPERATUREUNIT" "${unit}"
|
||||
change_setting "TEMPERATUREUNIT" "${unit}"
|
||||
echo -e " ${TICK} Set temperature unit to ${unit}"
|
||||
}
|
||||
|
||||
HashPassword() {
|
||||
# Compute password hash twice to avoid rainbow table vulnerability
|
||||
return=$(echo -n ${1} | sha256sum | sed 's/\s.*$//')
|
||||
return=$(echo -n ${return} | sha256sum | sed 's/\s.*$//')
|
||||
echo ${return}
|
||||
# Compute password hash twice to avoid rainbow table vulnerability
|
||||
return=$(echo -n ${1} | sha256sum | sed 's/\s.*$//')
|
||||
return=$(echo -n ${return} | sha256sum | sed 's/\s.*$//')
|
||||
echo ${return}
|
||||
}
|
||||
|
||||
SetWebPassword() {
|
||||
if [ "${SUDO_USER}" == "www-data" ]; then
|
||||
echo "Security measure: user www-data is not allowed to change webUI password!"
|
||||
echo "Exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${SUDO_USER}" == "lighttpd" ]; then
|
||||
echo "Security measure: user lighttpd is not allowed to change webUI password!"
|
||||
echo "Exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( ${#args[2]} > 0 )) ; then
|
||||
readonly PASSWORD="${args[2]}"
|
||||
readonly CONFIRM="${PASSWORD}"
|
||||
else
|
||||
read -s -p "Enter New Password (Blank for no password): " PASSWORD
|
||||
echo ""
|
||||
|
||||
if [ "${PASSWORD}" == "" ]; then
|
||||
change_setting "WEBPASSWORD" ""
|
||||
echo "Password Removed"
|
||||
exit 0
|
||||
if [ "${SUDO_USER}" == "www-data" ]; then
|
||||
echo "Security measure: user www-data is not allowed to change webUI password!"
|
||||
echo "Exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -s -p "Confirm Password: " CONFIRM
|
||||
echo ""
|
||||
fi
|
||||
if [ "${SUDO_USER}" == "lighttpd" ]; then
|
||||
echo "Security measure: user lighttpd is not allowed to change webUI password!"
|
||||
echo "Exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${PASSWORD}" == "${CONFIRM}" ] ; then
|
||||
hash=$(HashPassword ${PASSWORD})
|
||||
# Save hash to file
|
||||
change_setting "WEBPASSWORD" "${hash}"
|
||||
echo "New password set"
|
||||
else
|
||||
echo "Passwords don't match. Your password has not been changed"
|
||||
exit 1
|
||||
fi
|
||||
if (( ${#args[2]} > 0 )) ; then
|
||||
readonly PASSWORD="${args[2]}"
|
||||
readonly CONFIRM="${PASSWORD}"
|
||||
else
|
||||
# Prevents a bug if the user presses Ctrl+C and it continues to hide the text typed.
|
||||
# So we reset the terminal via stty if the user does press Ctrl+C
|
||||
trap '{ echo -e "\nNo password will be set" ; stty sane ; exit 1; }' INT
|
||||
read -s -r -p "Enter New Password (Blank for no password): " PASSWORD
|
||||
echo ""
|
||||
|
||||
if [ "${PASSWORD}" == "" ]; then
|
||||
change_setting "WEBPASSWORD" ""
|
||||
echo -e " ${TICK} Password Removed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
read -s -r -p "Confirm Password: " CONFIRM
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "${PASSWORD}" == "${CONFIRM}" ] ; then
|
||||
# We do not wrap this in brackets, otherwise BASH will expand any appropriate syntax
|
||||
hash=$(HashPassword "$PASSWORD")
|
||||
# Save hash to file
|
||||
change_setting "WEBPASSWORD" "${hash}"
|
||||
echo -e " ${TICK} New password set"
|
||||
else
|
||||
echo -e " ${CROSS} Passwords don't match. Your password has not been changed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ProcessDNSSettings() {
|
||||
source "${setupVars}"
|
||||
source "${setupVars}"
|
||||
|
||||
delete_dnsmasq_setting "server"
|
||||
delete_dnsmasq_setting "server"
|
||||
|
||||
COUNTER=1
|
||||
while [[ 1 ]]; do
|
||||
var=PIHOLE_DNS_${COUNTER}
|
||||
if [ -z "${!var}" ]; then
|
||||
break;
|
||||
fi
|
||||
add_dnsmasq_setting "server" "${!var}"
|
||||
let COUNTER=COUNTER+1
|
||||
done
|
||||
COUNTER=1
|
||||
while [[ 1 ]]; do
|
||||
var=PIHOLE_DNS_${COUNTER}
|
||||
if [ -z "${!var}" ]; then
|
||||
break;
|
||||
fi
|
||||
add_dnsmasq_setting "server" "${!var}"
|
||||
let COUNTER=COUNTER+1
|
||||
done
|
||||
|
||||
delete_dnsmasq_setting "domain-needed"
|
||||
# The option LOCAL_DNS_PORT is deprecated
|
||||
# We apply it once more, and then convert it into the current format
|
||||
if [ ! -z "${LOCAL_DNS_PORT}" ]; then
|
||||
add_dnsmasq_setting "server" "127.0.0.1#${LOCAL_DNS_PORT}"
|
||||
add_setting "PIHOLE_DNS_${COUNTER}" "127.0.0.1#${LOCAL_DNS_PORT}"
|
||||
delete_setting "LOCAL_DNS_PORT"
|
||||
fi
|
||||
|
||||
if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then
|
||||
add_dnsmasq_setting "domain-needed"
|
||||
fi
|
||||
delete_dnsmasq_setting "domain-needed"
|
||||
|
||||
delete_dnsmasq_setting "bogus-priv"
|
||||
if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then
|
||||
add_dnsmasq_setting "domain-needed"
|
||||
fi
|
||||
|
||||
if [[ "${DNS_BOGUS_PRIV}" == true ]]; then
|
||||
add_dnsmasq_setting "bogus-priv"
|
||||
fi
|
||||
delete_dnsmasq_setting "bogus-priv"
|
||||
|
||||
delete_dnsmasq_setting "dnssec"
|
||||
delete_dnsmasq_setting "trust-anchor="
|
||||
if [[ "${DNS_BOGUS_PRIV}" == true ]]; then
|
||||
add_dnsmasq_setting "bogus-priv"
|
||||
fi
|
||||
|
||||
if [[ "${DNSSEC}" == true ]]; then
|
||||
echo "dnssec
|
||||
delete_dnsmasq_setting "dnssec"
|
||||
delete_dnsmasq_setting "trust-anchor="
|
||||
|
||||
if [[ "${DNSSEC}" == true ]]; then
|
||||
echo "dnssec
|
||||
trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
|
||||
trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D
|
||||
" >> "${dnsmasqconfig}"
|
||||
fi
|
||||
fi
|
||||
|
||||
delete_dnsmasq_setting "host-record"
|
||||
delete_dnsmasq_setting "host-record"
|
||||
|
||||
if [ ! -z "${HOSTRECORD}" ]; then
|
||||
add_dnsmasq_setting "host-record" "${HOSTRECORD}"
|
||||
fi
|
||||
if [ ! -z "${HOSTRECORD}" ]; then
|
||||
add_dnsmasq_setting "host-record" "${HOSTRECORD}"
|
||||
fi
|
||||
|
||||
# Setup interface listening behavior of dnsmasq
|
||||
delete_dnsmasq_setting "interface"
|
||||
delete_dnsmasq_setting "local-service"
|
||||
# Setup interface listening behavior of dnsmasq
|
||||
delete_dnsmasq_setting "interface"
|
||||
delete_dnsmasq_setting "local-service"
|
||||
|
||||
if [[ "${DNSMASQ_LISTENING}" == "all" ]]; then
|
||||
# Listen on all interfaces, permit all origins
|
||||
add_dnsmasq_setting "except-interface" "nonexisting"
|
||||
elif [[ "${DNSMASQ_LISTENING}" == "local" ]]; then
|
||||
# Listen only on all interfaces, but only local subnets
|
||||
add_dnsmasq_setting "local-service"
|
||||
else
|
||||
# Listen only on one interface
|
||||
add_dnsmasq_setting "interface" "${PIHOLE_INTERFACE}"
|
||||
fi
|
||||
if [[ "${DNSMASQ_LISTENING}" == "all" ]]; then
|
||||
# Listen on all interfaces, permit all origins
|
||||
add_dnsmasq_setting "except-interface" "nonexisting"
|
||||
elif [[ "${DNSMASQ_LISTENING}" == "local" ]]; then
|
||||
# Listen only on all interfaces, but only local subnets
|
||||
add_dnsmasq_setting "local-service"
|
||||
else
|
||||
# Listen only on one interface
|
||||
# Use eth0 as fallback interface if interface is missing in setupVars.conf
|
||||
if [ -z "${PIHOLE_INTERFACE}" ]; then
|
||||
PIHOLE_INTERFACE="eth0"
|
||||
fi
|
||||
|
||||
add_dnsmasq_setting "interface" "${PIHOLE_INTERFACE}"
|
||||
fi
|
||||
|
||||
if [[ "${CONDITIONAL_FORWARDING}" == true ]]; then
|
||||
add_dnsmasq_setting "server=/${CONDITIONAL_FORWARDING_DOMAIN}/${CONDITIONAL_FORWARDING_IP}"
|
||||
add_dnsmasq_setting "server=/${CONDITIONAL_FORWARDING_REVERSE}/${CONDITIONAL_FORWARDING_IP}"
|
||||
fi
|
||||
}
|
||||
|
||||
SetDNSServers() {
|
||||
# Save setting to file
|
||||
delete_setting "PIHOLE_DNS"
|
||||
IFS=',' read -r -a array <<< "${args[2]}"
|
||||
for index in "${!array[@]}"
|
||||
do
|
||||
add_setting "PIHOLE_DNS_$((index+1))" "${array[index]}"
|
||||
done
|
||||
# Save setting to file
|
||||
delete_setting "PIHOLE_DNS"
|
||||
IFS=',' read -r -a array <<< "${args[2]}"
|
||||
for index in "${!array[@]}"
|
||||
do
|
||||
add_setting "PIHOLE_DNS_$((index+1))" "${array[index]}"
|
||||
done
|
||||
|
||||
if [[ "${args[3]}" == "domain-needed" ]]; then
|
||||
change_setting "DNS_FQDN_REQUIRED" "true"
|
||||
else
|
||||
change_setting "DNS_FQDN_REQUIRED" "false"
|
||||
fi
|
||||
if [[ "${args[3]}" == "domain-needed" ]]; then
|
||||
change_setting "DNS_FQDN_REQUIRED" "true"
|
||||
else
|
||||
change_setting "DNS_FQDN_REQUIRED" "false"
|
||||
fi
|
||||
|
||||
if [[ "${args[4]}" == "bogus-priv" ]]; then
|
||||
change_setting "DNS_BOGUS_PRIV" "true"
|
||||
else
|
||||
change_setting "DNS_BOGUS_PRIV" "false"
|
||||
fi
|
||||
if [[ "${args[4]}" == "bogus-priv" ]]; then
|
||||
change_setting "DNS_BOGUS_PRIV" "true"
|
||||
else
|
||||
change_setting "DNS_BOGUS_PRIV" "false"
|
||||
fi
|
||||
|
||||
if [[ "${args[5]}" == "dnssec" ]]; then
|
||||
change_setting "DNSSEC" "true"
|
||||
else
|
||||
change_setting "DNSSEC" "false"
|
||||
fi
|
||||
if [[ "${args[5]}" == "dnssec" ]]; then
|
||||
change_setting "DNSSEC" "true"
|
||||
else
|
||||
change_setting "DNSSEC" "false"
|
||||
fi
|
||||
|
||||
ProcessDNSSettings
|
||||
if [[ "${args[6]}" == "conditional_forwarding" ]]; then
|
||||
change_setting "CONDITIONAL_FORWARDING" "true"
|
||||
change_setting "CONDITIONAL_FORWARDING_IP" "${args[7]}"
|
||||
change_setting "CONDITIONAL_FORWARDING_DOMAIN" "${args[8]}"
|
||||
change_setting "CONDITIONAL_FORWARDING_REVERSE" "${args[9]}"
|
||||
else
|
||||
change_setting "CONDITIONAL_FORWARDING" "false"
|
||||
delete_setting "CONDITIONAL_FORWARDING_IP"
|
||||
delete_setting "CONDITIONAL_FORWARDING_DOMAIN"
|
||||
delete_setting "CONDITIONAL_FORWARDING_REVERSE"
|
||||
fi
|
||||
|
||||
# Restart dnsmasq to load new configuration
|
||||
RestartDNS
|
||||
ProcessDNSSettings
|
||||
|
||||
# Restart dnsmasq to load new configuration
|
||||
RestartDNS
|
||||
}
|
||||
|
||||
SetExcludeDomains() {
|
||||
change_setting "API_EXCLUDE_DOMAINS" "${args[2]}"
|
||||
change_setting "API_EXCLUDE_DOMAINS" "${args[2]}"
|
||||
}
|
||||
|
||||
SetExcludeClients() {
|
||||
change_setting "API_EXCLUDE_CLIENTS" "${args[2]}"
|
||||
change_setting "API_EXCLUDE_CLIENTS" "${args[2]}"
|
||||
}
|
||||
|
||||
Poweroff(){
|
||||
nohup bash -c "sleep 5; poweroff" &> /dev/null </dev/null &
|
||||
}
|
||||
|
||||
Reboot() {
|
||||
nohup bash -c "sleep 5; reboot" &> /dev/null </dev/null &
|
||||
nohup bash -c "sleep 5; reboot" &> /dev/null </dev/null &
|
||||
}
|
||||
|
||||
RestartDNS() {
|
||||
if [ -x "$(command -v systemctl)" ]; then
|
||||
systemctl restart dnsmasq &> /dev/null
|
||||
else
|
||||
service dnsmasq restart &> /dev/null
|
||||
fi
|
||||
/usr/local/bin/pihole restartdns
|
||||
}
|
||||
|
||||
SetQueryLogOptions() {
|
||||
change_setting "API_QUERY_LOG_SHOW" "${args[2]}"
|
||||
change_setting "API_QUERY_LOG_SHOW" "${args[2]}"
|
||||
}
|
||||
|
||||
ProcessDHCPSettings() {
|
||||
source "${setupVars}"
|
||||
source "${setupVars}"
|
||||
|
||||
if [[ "${DHCP_ACTIVE}" == "true" ]]; then
|
||||
interface=$(grep 'PIHOLE_INTERFACE=' /etc/pihole/setupVars.conf | sed "s/.*=//")
|
||||
if [[ "${DHCP_ACTIVE}" == "true" ]]; then
|
||||
interface="${PIHOLE_INTERFACE}"
|
||||
|
||||
# Use eth0 as fallback interface
|
||||
if [ -z ${interface} ]; then
|
||||
interface="eth0"
|
||||
interface="eth0"
|
||||
fi
|
||||
|
||||
if [[ "${PIHOLE_DOMAIN}" == "" ]]; then
|
||||
PIHOLE_DOMAIN="local"
|
||||
change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}"
|
||||
PIHOLE_DOMAIN="lan"
|
||||
change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}"
|
||||
fi
|
||||
|
||||
if [[ "${DHCP_LEASETIME}" == "0" ]]; then
|
||||
leasetime="infinite"
|
||||
leasetime="infinite"
|
||||
elif [[ "${DHCP_LEASETIME}" == "" ]]; then
|
||||
leasetime="24h"
|
||||
change_setting "DHCP_LEASETIME" "${leasetime}"
|
||||
leasetime="24"
|
||||
change_setting "DHCP_LEASETIME" "${leasetime}"
|
||||
elif [[ "${DHCP_LEASETIME}" == "24h" ]]; then
|
||||
#Installation is affected by known bug, introduced in a previous version.
|
||||
#This will automatically clean up setupVars.conf and remove the unnecessary "h"
|
||||
leasetime="24"
|
||||
change_setting "DHCP_LEASETIME" "${leasetime}"
|
||||
else
|
||||
leasetime="${DHCP_LEASETIME}h"
|
||||
leasetime="${DHCP_LEASETIME}h"
|
||||
fi
|
||||
|
||||
# Write settings to file
|
||||
@@ -261,12 +323,12 @@ dhcp-leasefile=/etc/pihole/dhcp.leases
|
||||
#quiet-dhcp
|
||||
" > "${dhcpconfig}"
|
||||
|
||||
if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then
|
||||
echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}"
|
||||
fi
|
||||
if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then
|
||||
echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}"
|
||||
fi
|
||||
|
||||
if [[ "${DHCP_IPv6}" == "true" ]]; then
|
||||
echo "#quiet-dhcp6
|
||||
echo "#quiet-dhcp6
|
||||
#enable-ra
|
||||
dhcp-option=option6:dns-server,[::]
|
||||
dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,${leasetime}
|
||||
@@ -274,124 +336,160 @@ ra-param=*,0,0
|
||||
" >> "${dhcpconfig}"
|
||||
fi
|
||||
|
||||
else
|
||||
rm "${dhcpconfig}" &> /dev/null
|
||||
fi
|
||||
else
|
||||
if [[ -f "${dhcpconfig}" ]]; then
|
||||
rm "${dhcpconfig}" &> /dev/null
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
EnableDHCP() {
|
||||
change_setting "DHCP_ACTIVE" "true"
|
||||
change_setting "DHCP_START" "${args[2]}"
|
||||
change_setting "DHCP_END" "${args[3]}"
|
||||
change_setting "DHCP_ROUTER" "${args[4]}"
|
||||
change_setting "DHCP_LEASETIME" "${args[5]}"
|
||||
change_setting "PIHOLE_DOMAIN" "${args[6]}"
|
||||
change_setting "DHCP_IPv6" "${args[7]}"
|
||||
change_setting "DHCP_ACTIVE" "true"
|
||||
change_setting "DHCP_START" "${args[2]}"
|
||||
change_setting "DHCP_END" "${args[3]}"
|
||||
change_setting "DHCP_ROUTER" "${args[4]}"
|
||||
change_setting "DHCP_LEASETIME" "${args[5]}"
|
||||
change_setting "PIHOLE_DOMAIN" "${args[6]}"
|
||||
change_setting "DHCP_IPv6" "${args[7]}"
|
||||
|
||||
# Remove possible old setting from file
|
||||
delete_dnsmasq_setting "dhcp-"
|
||||
delete_dnsmasq_setting "quiet-dhcp"
|
||||
# Remove possible old setting from file
|
||||
delete_dnsmasq_setting "dhcp-"
|
||||
delete_dnsmasq_setting "quiet-dhcp"
|
||||
|
||||
ProcessDHCPSettings
|
||||
ProcessDHCPSettings
|
||||
|
||||
RestartDNS
|
||||
RestartDNS
|
||||
}
|
||||
|
||||
DisableDHCP() {
|
||||
change_setting "DHCP_ACTIVE" "false"
|
||||
change_setting "DHCP_ACTIVE" "false"
|
||||
|
||||
# Remove possible old setting from file
|
||||
delete_dnsmasq_setting "dhcp-"
|
||||
delete_dnsmasq_setting "quiet-dhcp"
|
||||
# Remove possible old setting from file
|
||||
delete_dnsmasq_setting "dhcp-"
|
||||
delete_dnsmasq_setting "quiet-dhcp"
|
||||
|
||||
ProcessDHCPSettings
|
||||
ProcessDHCPSettings
|
||||
|
||||
RestartDNS
|
||||
RestartDNS
|
||||
}
|
||||
|
||||
SetWebUILayout() {
|
||||
change_setting "WEBUIBOXEDLAYOUT" "${args[2]}"
|
||||
change_setting "WEBUIBOXEDLAYOUT" "${args[2]}"
|
||||
}
|
||||
|
||||
CustomizeAdLists() {
|
||||
list="/etc/pihole/adlists.list"
|
||||
list="/etc/pihole/adlists.list"
|
||||
|
||||
if [[ "${args[2]}" == "enable" ]]; then
|
||||
sed -i "\\@${args[3]}@s/^#http/http/g" "${list}"
|
||||
elif [[ "${args[2]}" == "disable" ]]; then
|
||||
sed -i "\\@${args[3]}@s/^http/#http/g" "${list}"
|
||||
elif [[ "${args[2]}" == "add" ]]; then
|
||||
echo "${args[3]}" >> ${list}
|
||||
elif [[ "${args[2]}" == "del" ]]; then
|
||||
var=$(echo "${args[3]}" | sed 's/\//\\\//g')
|
||||
sed -i "/${var}/Id" "${list}"
|
||||
else
|
||||
echo "Not permitted"
|
||||
return 1
|
||||
fi
|
||||
if [[ "${args[2]}" == "enable" ]]; then
|
||||
sed -i "\\@${args[3]}@s/^#http/http/g" "${list}"
|
||||
elif [[ "${args[2]}" == "disable" ]]; then
|
||||
sed -i "\\@${args[3]}@s/^http/#http/g" "${list}"
|
||||
elif [[ "${args[2]}" == "add" ]]; then
|
||||
if [[ $(grep -c "^${args[3]}$" "${list}") -eq 0 ]] ; then
|
||||
echo "${args[3]}" >> ${list}
|
||||
fi
|
||||
elif [[ "${args[2]}" == "del" ]]; then
|
||||
var=$(echo "${args[3]}" | sed 's/\//\\\//g')
|
||||
sed -i "/${var}/Id" "${list}"
|
||||
else
|
||||
echo "Not permitted"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
SetPrivacyMode() {
|
||||
if [[ "${args[2]}" == "true" ]]; then
|
||||
change_setting "API_PRIVACY_MODE" "true"
|
||||
else
|
||||
change_setting "API_PRIVACY_MODE" "false"
|
||||
fi
|
||||
if [[ "${args[2]}" == "true" ]]; then
|
||||
change_setting "API_PRIVACY_MODE" "true"
|
||||
else
|
||||
change_setting "API_PRIVACY_MODE" "false"
|
||||
fi
|
||||
}
|
||||
|
||||
ResolutionSettings() {
|
||||
typ="${args[2]}"
|
||||
state="${args[3]}"
|
||||
typ="${args[2]}"
|
||||
state="${args[3]}"
|
||||
|
||||
if [[ "${typ}" == "forward" ]]; then
|
||||
change_setting "API_GET_UPSTREAM_DNS_HOSTNAME" "${state}"
|
||||
elif [[ "${typ}" == "clients" ]]; then
|
||||
change_setting "API_GET_CLIENT_HOSTNAME" "${state}"
|
||||
fi
|
||||
if [[ "${typ}" == "forward" ]]; then
|
||||
change_setting "API_GET_UPSTREAM_DNS_HOSTNAME" "${state}"
|
||||
elif [[ "${typ}" == "clients" ]]; then
|
||||
change_setting "API_GET_CLIENT_HOSTNAME" "${state}"
|
||||
fi
|
||||
}
|
||||
|
||||
AddDHCPStaticAddress() {
|
||||
mac="${args[2]}"
|
||||
ip="${args[3]}"
|
||||
host="${args[4]}"
|
||||
mac="${args[2]}"
|
||||
ip="${args[3]}"
|
||||
host="${args[4]}"
|
||||
|
||||
if [[ "${ip}" == "noip" ]]; then
|
||||
# Static host name
|
||||
echo "dhcp-host=${mac},${host}" >> "${dhcpstaticconfig}"
|
||||
elif [[ "${host}" == "nohost" ]]; then
|
||||
# Static IP
|
||||
echo "dhcp-host=${mac},${ip}" >> "${dhcpstaticconfig}"
|
||||
else
|
||||
# Full info given
|
||||
echo "dhcp-host=${mac},${ip},${host}" >> "${dhcpstaticconfig}"
|
||||
fi
|
||||
if [[ "${ip}" == "noip" ]]; then
|
||||
# Static host name
|
||||
echo "dhcp-host=${mac},${host}" >> "${dhcpstaticconfig}"
|
||||
elif [[ "${host}" == "nohost" ]]; then
|
||||
# Static IP
|
||||
echo "dhcp-host=${mac},${ip}" >> "${dhcpstaticconfig}"
|
||||
else
|
||||
# Full info given
|
||||
echo "dhcp-host=${mac},${ip},${host}" >> "${dhcpstaticconfig}"
|
||||
fi
|
||||
}
|
||||
|
||||
RemoveDHCPStaticAddress() {
|
||||
mac="${args[2]}"
|
||||
sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}"
|
||||
mac="${args[2]}"
|
||||
sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}"
|
||||
}
|
||||
|
||||
SetHostRecord() {
|
||||
if [ -n "${args[3]}" ]; then
|
||||
change_setting "HOSTRECORD" "${args[2]},${args[3]}"
|
||||
echo "Setting host record for ${args[2]} -> ${args[3]}"
|
||||
else
|
||||
change_setting "HOSTRECORD" ""
|
||||
echo "Removing host record"
|
||||
fi
|
||||
if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then
|
||||
echo "Usage: pihole -a hostrecord <domain> [IPv4-address],[IPv6-address]
|
||||
Example: 'pihole -a hostrecord home.domain.com 192.168.1.1,2001:db8:a0b:12f0::1'
|
||||
Add a name to the DNS associated to an IPv4/IPv6 address
|
||||
|
||||
ProcessDNSSettings
|
||||
Options:
|
||||
\"\" Empty: Remove host record
|
||||
-h, --help Show this help dialog"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Restart dnsmasq to load new configuration
|
||||
RestartDNS
|
||||
if [[ -n "${args[3]}" ]]; then
|
||||
change_setting "HOSTRECORD" "${args[2]},${args[3]}"
|
||||
echo -e " ${TICK} Setting host record for ${args[2]} to ${args[3]}"
|
||||
else
|
||||
change_setting "HOSTRECORD" ""
|
||||
echo -e " ${TICK} Removing host record"
|
||||
fi
|
||||
|
||||
ProcessDNSSettings
|
||||
|
||||
# Restart dnsmasq to load new configuration
|
||||
RestartDNS
|
||||
}
|
||||
|
||||
SetAdminEmail() {
|
||||
if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then
|
||||
echo "Usage: pihole -a email <address>
|
||||
Example: 'pihole -a email admin@address.com'
|
||||
Set an administrative contact address for the Block Page
|
||||
|
||||
Options:
|
||||
\"\" Empty: Remove admin contact
|
||||
-h, --help Show this help dialog"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -n "${args[2]}" ]]; then
|
||||
change_setting "ADMIN_EMAIL" "${args[2]}"
|
||||
echo -e " ${TICK} Setting admin contact to ${args[2]}"
|
||||
else
|
||||
change_setting "ADMIN_EMAIL" ""
|
||||
echo -e " ${TICK} Removing admin contact"
|
||||
fi
|
||||
}
|
||||
|
||||
SetListeningMode() {
|
||||
source "${setupVars}"
|
||||
|
||||
if [[ "$3" == "-h" ]] || [[ "$3" == "--help" ]]; then
|
||||
echo "Usage: pihole -a -i [interface]
|
||||
source "${setupVars}"
|
||||
|
||||
if [[ "$3" == "-h" ]] || [[ "$3" == "--help" ]]; then
|
||||
echo "Usage: pihole -a -i [interface]
|
||||
Example: 'pihole -a -i local'
|
||||
Specify dnsmasq's network interface listening behavior
|
||||
|
||||
@@ -400,66 +498,93 @@ Interfaces:
|
||||
devices that are at most one hop away (local devices)
|
||||
single Listen only on ${PIHOLE_INTERFACE} interface
|
||||
all Listen on all interfaces, permit all origins"
|
||||
exit 0
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${args[2]}" == "all" ]]; then
|
||||
echo "Listening on all interfaces, permiting all origins, hope you have a firewall!"
|
||||
change_setting "DNSMASQ_LISTENING" "all"
|
||||
elif [[ "${args[2]}" == "local" ]]; then
|
||||
echo "Listening on all interfaces, permitting only origins that are at most one hop away (local devices)"
|
||||
change_setting "DNSMASQ_LISTENING" "local"
|
||||
else
|
||||
echo "Listening only on interface ${PIHOLE_INTERFACE}"
|
||||
change_setting "DNSMASQ_LISTENING" "single"
|
||||
fi
|
||||
|
||||
# Don't restart DNS server yet because other settings
|
||||
# will be applied afterwards if "-web" is set
|
||||
if [[ "${args[3]}" != "-web" ]]; then
|
||||
ProcessDNSSettings
|
||||
# Restart dnsmasq to load new configuration
|
||||
RestartDNS
|
||||
fi
|
||||
if [[ "${args[2]}" == "all" ]]; then
|
||||
echo -e " ${INFO} Listening on all interfaces, permiting all origins. Please use a firewall!"
|
||||
change_setting "DNSMASQ_LISTENING" "all"
|
||||
elif [[ "${args[2]}" == "local" ]]; then
|
||||
echo -e " ${INFO} Listening on all interfaces, permiting origins from one hop away (LAN)"
|
||||
change_setting "DNSMASQ_LISTENING" "local"
|
||||
else
|
||||
echo -e " ${INFO} Listening only on interface ${PIHOLE_INTERFACE}"
|
||||
change_setting "DNSMASQ_LISTENING" "single"
|
||||
fi
|
||||
|
||||
# Don't restart DNS server yet because other settings
|
||||
# will be applied afterwards if "-web" is set
|
||||
if [[ "${args[3]}" != "-web" ]]; then
|
||||
ProcessDNSSettings
|
||||
# Restart dnsmasq to load new configuration
|
||||
RestartDNS
|
||||
fi
|
||||
}
|
||||
|
||||
Teleporter() {
|
||||
local datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S")
|
||||
php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.zip"
|
||||
local datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S")
|
||||
php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.zip"
|
||||
}
|
||||
|
||||
addAudit()
|
||||
{
|
||||
shift # skip "-a"
|
||||
shift # skip "audit"
|
||||
for var in "$@"
|
||||
do
|
||||
echo "${var}" >> /etc/pihole/auditlog.list
|
||||
done
|
||||
}
|
||||
|
||||
clearAudit()
|
||||
{
|
||||
echo -n "" > /etc/pihole/auditlog.list
|
||||
}
|
||||
|
||||
SetPrivacyLevel() {
|
||||
# Set privacy level. Minimum is 0, maximum is 4
|
||||
if [ "${args[2]}" -ge 0 ] && [ "${args[2]}" -le 4 ]; then
|
||||
changeFTLsetting "PRIVACYLEVEL" "${args[2]}"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
args=("$@")
|
||||
args=("$@")
|
||||
|
||||
case "${args[1]}" in
|
||||
"-p" | "password" ) SetWebPassword;;
|
||||
"-c" | "celsius" ) unit="C"; SetTemperatureUnit;;
|
||||
"-f" | "fahrenheit" ) unit="F"; SetTemperatureUnit;;
|
||||
"-k" | "kelvin" ) unit="K"; SetTemperatureUnit;;
|
||||
"setdns" ) SetDNSServers;;
|
||||
"setexcludedomains" ) SetExcludeDomains;;
|
||||
"setexcludeclients" ) SetExcludeClients;;
|
||||
"reboot" ) Reboot;;
|
||||
"restartdns" ) RestartDNS;;
|
||||
"setquerylog" ) SetQueryLogOptions;;
|
||||
"enabledhcp" ) EnableDHCP;;
|
||||
"disabledhcp" ) DisableDHCP;;
|
||||
"layout" ) SetWebUILayout;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"privacymode" ) SetPrivacyMode;;
|
||||
"resolve" ) ResolutionSettings;;
|
||||
"addstaticdhcp" ) AddDHCPStaticAddress;;
|
||||
"removestaticdhcp" ) RemoveDHCPStaticAddress;;
|
||||
"hostrecord" ) SetHostRecord;;
|
||||
"-i" | "interface" ) SetListeningMode "$@";;
|
||||
"-t" | "teleporter" ) Teleporter;;
|
||||
"adlist" ) CustomizeAdLists;;
|
||||
* ) helpFunc;;
|
||||
esac
|
||||
case "${args[1]}" in
|
||||
"-p" | "password" ) SetWebPassword;;
|
||||
"-c" | "celsius" ) unit="C"; SetTemperatureUnit;;
|
||||
"-f" | "fahrenheit" ) unit="F"; SetTemperatureUnit;;
|
||||
"-k" | "kelvin" ) unit="K"; SetTemperatureUnit;;
|
||||
"setdns" ) SetDNSServers;;
|
||||
"setexcludedomains" ) SetExcludeDomains;;
|
||||
"setexcludeclients" ) SetExcludeClients;;
|
||||
"poweroff" ) Poweroff;;
|
||||
"reboot" ) Reboot;;
|
||||
"restartdns" ) RestartDNS;;
|
||||
"setquerylog" ) SetQueryLogOptions;;
|
||||
"enabledhcp" ) EnableDHCP;;
|
||||
"disabledhcp" ) DisableDHCP;;
|
||||
"layout" ) SetWebUILayout;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"privacymode" ) SetPrivacyMode;;
|
||||
"resolve" ) ResolutionSettings;;
|
||||
"addstaticdhcp" ) AddDHCPStaticAddress;;
|
||||
"removestaticdhcp" ) RemoveDHCPStaticAddress;;
|
||||
"-r" | "hostrecord" ) SetHostRecord "$3";;
|
||||
"-e" | "email" ) SetAdminEmail "$3";;
|
||||
"-i" | "interface" ) SetListeningMode "$@";;
|
||||
"-t" | "teleporter" ) Teleporter;;
|
||||
"adlist" ) CustomizeAdLists;;
|
||||
"audit" ) addAudit "$@";;
|
||||
"clearaudit" ) clearAudit;;
|
||||
"-l" | "privacylevel" ) SetPrivacyLevel;;
|
||||
* ) helpFunc;;
|
||||
esac
|
||||
|
||||
shift
|
||||
shift
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
helpFunc
|
||||
fi
|
||||
if [[ $# = 0 ]]; then
|
||||
helpFunc
|
||||
fi
|
||||
}
|
||||
|
28
advanced/Scripts/wildcard_regex_converter.sh
Normal file
28
advanced/Scripts/wildcard_regex_converter.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Provides an automated migration subroutine to convert Pi-hole v3.x wildcard domains to Pi-hole v4.x regex filters
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
# regexFile set in gravity.sh
|
||||
|
||||
wildcardFile="/etc/dnsmasq.d/03-pihole-wildcard.conf"
|
||||
|
||||
convert_wildcard_to_regex() {
|
||||
if [ ! -f "${wildcardFile}" ]; then
|
||||
return
|
||||
fi
|
||||
local addrlines domains uniquedomains
|
||||
# Obtain wildcard domains from old file
|
||||
addrlines="$(grep -oE "/.*/" ${wildcardFile})"
|
||||
# Strip "/" from domain names and convert "." to regex-compatible "\."
|
||||
domains="$(sed 's/\///g;s/\./\\./g' <<< "${addrlines}")"
|
||||
# Remove repeated domains (may have been inserted two times due to A and AAAA blocking)
|
||||
uniquedomains="$(uniq <<< "${domains}")"
|
||||
# Automatically generate regex filters and remove old wildcards file
|
||||
awk '{print "(^|\\.)"$0"$"}' <<< "${uniquedomains}" >> "${regexFile:?}" && rm "${wildcardFile}"
|
||||
}
|
@@ -20,15 +20,32 @@ is_running() {
|
||||
ps "$(get_pid)" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
|
||||
# Start the service
|
||||
start() {
|
||||
if is_running; then
|
||||
echo "pihole-FTL is already running"
|
||||
else
|
||||
touch /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port
|
||||
chown pihole:pihole /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /etc/pihole
|
||||
chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port
|
||||
su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER"
|
||||
# Touch files to ensure they exist (create if non-existing, preserve if existing)
|
||||
touch /var/log/pihole-FTL.log /var/log/pihole.log
|
||||
touch /run/pihole-FTL.pid /run/pihole-FTL.port
|
||||
touch /etc/pihole/dhcp.leases
|
||||
mkdir -p /var/run/pihole
|
||||
mkdir -p /var/log/pihole
|
||||
chown pihole:pihole /var/run/pihole /var/log/pihole
|
||||
rm /var/run/pihole/FTL.sock 2> /dev/null
|
||||
# Ensure that permissions are set so that pihole-FTL can edit all necessary files
|
||||
chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port
|
||||
chown pihole:pihole /etc/pihole /etc/pihole/dhcp.leases 2> /dev/null
|
||||
chown pihole:pihole /var/log/pihole-FTL.log /var/log/pihole.log
|
||||
chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log
|
||||
echo "nameserver 127.0.0.1" | /sbin/resolvconf -a lo.piholeFTL
|
||||
if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN+eip "$(which pihole-FTL)"; then
|
||||
su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER"
|
||||
else
|
||||
echo "Warning: Starting pihole-FTL as root because setting capabilities is not supported on this system"
|
||||
pihole-FTL
|
||||
fi
|
||||
echo
|
||||
fi
|
||||
}
|
||||
@@ -36,6 +53,7 @@ start() {
|
||||
# Stop the service
|
||||
stop() {
|
||||
if is_running; then
|
||||
/sbin/resolvconf -d lo.piholeFTL
|
||||
kill "$(get_pid)"
|
||||
for i in {1..5}; do
|
||||
if ! is_running; then
|
||||
@@ -60,13 +78,25 @@ stop() {
|
||||
echo
|
||||
}
|
||||
|
||||
# Indicate the service status
|
||||
status() {
|
||||
if is_running; then
|
||||
echo "[ ok ] pihole-FTL is running"
|
||||
exit 0
|
||||
else
|
||||
echo "[ ] pihole-FTL is not running"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
### main logic ###
|
||||
case "$1" in
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status pihole-FTL
|
||||
status
|
||||
;;
|
||||
start|restart|reload|condrestart)
|
||||
stop
|
@@ -14,12 +14,11 @@
|
||||
# is updated or re-installed. Please make any changes to the appropriate crontab
|
||||
# or other cron file snippets.
|
||||
|
||||
# Pi-hole: Update the ad sources once a week on Sunday at 01:59
|
||||
# Download any updates from the adlists
|
||||
59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity
|
||||
|
||||
# Pi-hole: Update Pi-hole! Uncomment to enable auto update
|
||||
#30 2 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updatePihole
|
||||
# Pi-hole: Update the ad sources once a week on Sunday at a random time in the
|
||||
# early morning. Download any updates from the adlists
|
||||
# Squash output to log, then splat the log to stdout on error to allow for
|
||||
# standard crontab job error handling.
|
||||
59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity >/var/log/pihole_updateGravity.log || cat /var/log/pihole_updateGravity.log
|
||||
|
||||
# Pi-hole: Flush the log daily at 00:00
|
||||
# The flush script will use logrotate if available
|
||||
@@ -28,3 +27,10 @@
|
||||
00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush once quiet
|
||||
|
||||
@reboot root /usr/sbin/logrotate /etc/pihole/logrotate
|
||||
|
||||
# Pi-hole: Grab local version and branch every 10 minutes
|
||||
*/10 * * * * root PATH="$PATH:/usr/local/bin/" pihole updatechecker local
|
||||
|
||||
# Pi-hole: Grab remote version every 24 hours
|
||||
59 17 * * * root PATH="$PATH:/usr/local/bin/" pihole updatechecker remote
|
||||
@reboot root PATH="$PATH:/usr/local/bin/" pihole updatechecker remote reboot
|
@@ -1,11 +1,79 @@
|
||||
_pihole() {
|
||||
local cur prev opts
|
||||
local cur prev opts opts_admin opts_checkout opts_chronometer opts_debug opts_interface opts_logging opts_privacy opts_query opts_update opts_version
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
opts="admin blacklist chronometer debug disable enable flush help logging query reconfigure restartdns setupLCD status tail uninstall updateGravity updatePihole version whitelist checkout"
|
||||
prev2="${COMP_WORDS[COMP_CWORD-2]}"
|
||||
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
case "${prev}" in
|
||||
"pihole")
|
||||
opts="admin blacklist checkout chronometer debug disable enable flush help logging query reconfigure regex restartdns status tail uninstall updateGravity updatePihole version wildcard whitelist"
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
;;
|
||||
"whitelist"|"blacklist"|"wildcard"|"regex")
|
||||
opts_lists="\--delmode \--noreload \--quiet \--list \--nuke"
|
||||
COMPREPLY=( $(compgen -W "${opts_lists}" -- ${cur}) )
|
||||
;;
|
||||
"admin")
|
||||
opts_admin="celsius email fahrenheit hostrecord interface kelvin password privacylevel"
|
||||
COMPREPLY=( $(compgen -W "${opts_admin}" -- ${cur}) )
|
||||
;;
|
||||
"checkout")
|
||||
opts_checkout="core ftl web master dev"
|
||||
COMPREPLY=( $(compgen -W "${opts_checkout}" -- ${cur}) )
|
||||
;;
|
||||
"chronometer")
|
||||
opts_chronometer="\--exit \--json \--refresh"
|
||||
COMPREPLY=( $(compgen -W "${opts_chronometer}" -- ${cur}) )
|
||||
;;
|
||||
"debug")
|
||||
opts_debug="-a"
|
||||
COMPREPLY=( $(compgen -W "${opts_debug}" -- ${cur}) )
|
||||
;;
|
||||
"logging")
|
||||
opts_logging="on off 'off noflush'"
|
||||
COMPREPLY=( $(compgen -W "${opts_logging}" -- ${cur}) )
|
||||
;;
|
||||
"query")
|
||||
opts_query="-adlist -all -exact"
|
||||
COMPREPLY=( $(compgen -W "${opts_query}" -- ${cur}) )
|
||||
;;
|
||||
"updatePihole"|"-up")
|
||||
opts_update="--check-only"
|
||||
COMPREPLY=( $(compgen -W "${opts_update}" -- ${cur}) )
|
||||
;;
|
||||
"version")
|
||||
opts_version="\--admin \--current \--ftl \--hash \--latest \--pihole"
|
||||
COMPREPLY=( $(compgen -W "${opts_version}" -- ${cur}) )
|
||||
;;
|
||||
"interface")
|
||||
if ( [[ "$prev2" == "admin" ]] || [[ "$prev2" == "-a" ]] ); then
|
||||
opts_interface="$(cat /proc/net/dev | cut -d: -s -f1)"
|
||||
COMPREPLY=( $(compgen -W "${opts_interface}" -- ${cur}) )
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
"privacylevel")
|
||||
if ( [[ "$prev2" == "admin" ]] || [[ "$prev2" == "-a" ]] ); then
|
||||
opts_privacy="0 1 2 3"
|
||||
COMPREPLY=( $(compgen -W "${opts_privacy}" -- ${cur}) )
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
"core"|"admin"|"ftl")
|
||||
if [[ "$prev2" == "checkout" ]]; then
|
||||
opts_checkout="master dev"
|
||||
COMPREPLY=( $(compgen -W "${opts_checkout}" -- ${cur}) )
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _pihole pihole
|
||||
|
@@ -1,136 +1,383 @@
|
||||
/* CSS Reset */
|
||||
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; }
|
||||
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; }
|
||||
body { line-height: 1; }
|
||||
ol, ul { list-style: none; }
|
||||
blockquote, q { quotes: none; }
|
||||
blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; }
|
||||
table { border-collapse: collapse; border-spacing: 0; }
|
||||
html { height: 100%; overflow-x: hidden; }
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* General Style */
|
||||
a { color: rgba(0,60,120,0.95); text-decoration: none; } /* 1E3C5A */
|
||||
a:hover { color: rgba(210,120,0,0.95); transition-duration: .2s; } /* 255, 128, 0 */
|
||||
divs a { border-bottom: 1px dashed rgba(30,60,90,0.3); }
|
||||
b { font-weight: bold; }
|
||||
i { font-style: italic; }
|
||||
/* Text Customisation Options ======> */
|
||||
.title:before { content: "Website Blocked"; }
|
||||
.altBtn:before { content: "Why am I here?"; }
|
||||
.linkPH:before { content: "About Pi-hole"; }
|
||||
.linkEmail:before { content: "Contact Admin"; }
|
||||
|
||||
footer, pre, td { font-family: monospace; padding-left: 15px; }
|
||||
/*body, header { background: #E1E1E1; }*/
|
||||
#bpOutput.add:before { content: "Info"; }
|
||||
#bpOutput.add:after { content: "The domain is being whitelisted..."; }
|
||||
#bpOutput.error:before, .unhandled:before { content: "Error"; }
|
||||
#bpOutput.unhandled:after { content: "An unhandled exception occured. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; }
|
||||
#bpOutput.success:before { content: "Success"; }
|
||||
#bpOutput.success:after { content: "Website has been whitelisted! You may need to flush your DNS cache"; }
|
||||
|
||||
.recentwl:before { content: "This site has been whitelisted. Please flush your DNS cache and/or restart your browser."; }
|
||||
.unknown:before { content: "This website is not found in any of Pi-hole's blacklists. The reason you have arrived here is unknown."; }
|
||||
.cname:before { content: "This site is an alias for "; } /* <a href="http://cname.com">cname.com</a> */
|
||||
.cname:after { content: ", which may be blocked by Pi-hole."; }
|
||||
|
||||
.blacklist:before { content: "Manually Blacklisted"; }
|
||||
.wildcard:before { content: "Manually Blacklisted by Wildcard"; }
|
||||
.noblock:before { content: "Not found on any Blacklist"; }
|
||||
|
||||
#bpBlock:before { content: "Access to the following website has been denied:"; }
|
||||
#bpFlag:before { content: "This is primarily due to being flagged as:"; }
|
||||
|
||||
#bpHelpTxt:before { content: "If you have an ongoing use for this website, please "; }
|
||||
#bpHelpTxt a:before, #bpHelpTxt span:before { content: "ask the administrator"; }
|
||||
#bpHelpTxt:after{ content: " of the Pi-hole on this network to have it whitelisted"; }
|
||||
|
||||
#bpBack:before { content: "Back to safety"; }
|
||||
#bpInfo:before { content: "Technical Info"; }
|
||||
#bpFoundIn:before { content: "This site is found in "; }
|
||||
#bpFoundIn span:after { content: " of "; }
|
||||
#bpFoundIn:after { content: " lists:"; }
|
||||
#bpWhitelist:before { content: "Whitelist"; }
|
||||
|
||||
footer span:before { content: "Page generated on "; }
|
||||
|
||||
/* Hide whitelisting form entirely */
|
||||
/* #bpWLButtons { display: none; } */
|
||||
/* Text Customisation Options <=============================== */
|
||||
|
||||
/* http://necolas.github.io/normalize.css ======> */
|
||||
html { font-family: sans-serif; line-height: 1.15; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; }
|
||||
body { margin: 0; }
|
||||
article, aside, footer, header, nav, section { display: block; }
|
||||
h1 { font-size: 2em; margin: 0.67em 0; }
|
||||
figcaption, figure, main { display: block; }
|
||||
figure { margin: 1em 40px; }
|
||||
hr { box-sizing: content-box; height: 0; overflow: visible; }
|
||||
pre { font-family: monospace, monospace; font-size: 1em; }
|
||||
a { background-color: transparent; -webkit-text-decoration-skip: objects; }
|
||||
a:active, a:hover { outline-width: 0; }
|
||||
abbr[title] { border-bottom: none; text-decoration: underline; text-decoration: underline dotted; }
|
||||
b, strong { font-weight: inherit; }
|
||||
b, strong { font-weight: bolder; }
|
||||
code, kbd, samp { font-family: monospace, monospace; font-size: 1em; }
|
||||
dfn { font-style: italic; }
|
||||
mark { background-color: #ff0; color: #000; }
|
||||
small { font-size: 80%; }
|
||||
sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
|
||||
sub { bottom: -0.25em; }
|
||||
sup { top: -0.5em; }
|
||||
audio, video { display: inline-block; }
|
||||
audio:not([controls]) { display: none; height: 0; }
|
||||
img { border-style: none; }
|
||||
svg:not(:root) { overflow: hidden; }
|
||||
button, input, optgroup, select, textarea { font-family: sans-serif; font-size: 100%; line-height: 1.15; margin: 0; }
|
||||
button, input { overflow: visible; }
|
||||
button, select { text-transform: none; }
|
||||
button, html [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; }
|
||||
button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { border-style: none; padding: 0; }
|
||||
button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring { outline: 1px dotted ButtonText; }
|
||||
fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
|
||||
legend { box-sizing: border-box; color: inherit; display: table; max-width: 100%; padding: 0; white-space: normal; }
|
||||
progress { display: inline-block; vertical-align: baseline; }
|
||||
textarea { overflow: auto; }
|
||||
[type="checkbox"], [type="radio"] { box-sizing: border-box; padding: 0; }
|
||||
[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { height: auto; }
|
||||
[type="search"] { -webkit-appearance: textfield; outline-offset: -2px; }
|
||||
[type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
|
||||
::-webkit-file-upload-button { -webkit-appearance: button; font: inherit; }
|
||||
details, menu { display: block; }
|
||||
summary { display: list-item; }
|
||||
canvas { display: inline-block; }
|
||||
template { display: none; }
|
||||
[hidden] { display: none; }
|
||||
/* Normalize.css <=============================== */
|
||||
|
||||
html { font-size: 62.5%; }
|
||||
|
||||
a { color: #3c8dbc; text-decoration: none; }
|
||||
a:hover { color: #72afda; text-decoration: underline; }
|
||||
b { color: rgb(68,68,68); }
|
||||
p { margin: 0; }
|
||||
|
||||
label, .buttons a {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
label, .buttons *:not([disabled]) { cursor: pointer; }
|
||||
|
||||
/* Touch device dark tap highlight */
|
||||
header h1 a, label, .buttons * { -webkit-tap-highlight-color: transparent; }
|
||||
|
||||
/* Webkit Focus Glow */
|
||||
textarea, input, button { outline: none; }
|
||||
|
||||
@font-face {
|
||||
font-family: "Source Sans Pro";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Source Sans Pro"), local("SourceSansPro-Regular"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Regular.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Source Sans Pro";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local("Source Sans Pro Bold"), local("SourceSansPro-Bold"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Bold.ttf") format("truetype");
|
||||
}
|
||||
|
||||
body {
|
||||
background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(190,190,190,0.95));
|
||||
background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(190,190,190,0.95));
|
||||
background-attachment: fixed;
|
||||
color: rgba(64,64,64,0.95);
|
||||
font: 14px, sans-serif;
|
||||
line-height: 1em;
|
||||
background: #dbdbdb url("/admin/img/boxed-bg.jpg") repeat fixed;
|
||||
color: #333;
|
||||
font: 1.4rem "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
line-height: 2.2rem;
|
||||
}
|
||||
|
||||
/* User is greeted with a splash page when browsing to Pi-hole IP address */
|
||||
#splashpage { background: #222; color: rgba(255,255,255,0.7); text-align: center; }
|
||||
#splashpage img { margin: 5px; width: 256px; }
|
||||
#splashpage b { color: inherit; }
|
||||
|
||||
#bpWrapper {
|
||||
margin: 0 auto;
|
||||
max-width: 1250px;
|
||||
box-shadow: 0 0 8px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
header {
|
||||
min-width: 320px;
|
||||
width: 100%;
|
||||
text-shadow: 0 1px rgba(255,255,255,0.6);
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
border: 1px solid rgba(0,0,0,0.25);
|
||||
border-top-color: rgba(255,255,255,0.85);
|
||||
border-style: solid none;
|
||||
background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(220,220,220,0.95));
|
||||
background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(220,220,220,0.95));
|
||||
box-shadow: 0 0 1px 1px rgba(0,0,0,0.04);
|
||||
background: #3c8dbc;
|
||||
display: table;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
header h1, header div {
|
||||
display: table-cell;
|
||||
color: inherit;
|
||||
font-weight: bold;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
header h1, header h1 a, header .spc, header #bpAlt label {
|
||||
display: table-cell;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
height: 50px; /* Must match #bpAbout top value */
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
padding: 8px 0;
|
||||
text-indent: 32px;
|
||||
background: url("http://pi.hole/admin/img/logo.svg") left no-repeat;
|
||||
background-size: 30px 22px;
|
||||
h1 a {
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
font-family: "Helvetica Neue", Helvetica, Arial ,sans-serif;
|
||||
font-size: 2rem;
|
||||
font-weight: normal;
|
||||
min-width: 230px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header h1 a, h1 a:hover { color: inherit; }
|
||||
header .alt { width: 85px; font-size: 0.8em; padding-right: 4px; text-align: right; line-height: 1.25em; }
|
||||
.active { color: green; }
|
||||
.inactive { color: red; }
|
||||
h1 a:hover, header #bpAlt:hover { background-color: rgba(0,0,0,0.12); color: inherit; text-decoration: none; }
|
||||
|
||||
header .spc { width: 100%; }
|
||||
|
||||
header #bpAlt label {
|
||||
background: url("/admin/img/logo.svg") no-repeat center left 15px;
|
||||
background-size: 15px 23px;
|
||||
padding: 0 15px;
|
||||
text-indent: 30px;
|
||||
}
|
||||
|
||||
[type=checkbox][id$="Toggle"] { display: none; }
|
||||
[type=checkbox][id$="Toggle"]:checked ~ #bpAbout,
|
||||
[type=checkbox][id$="Toggle"]:checked ~ #bpMoreInfo {
|
||||
display: block; }
|
||||
|
||||
/* Click anywhere else on screen to hide #bpAbout */
|
||||
#bpAboutToggle:checked {
|
||||
display: block;
|
||||
height: 300px; /* VH Fallback */
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#bpAbout {
|
||||
background: #3c8dbc;
|
||||
border-bottom-left-radius: 5px;
|
||||
border: 1px solid #FFF;
|
||||
border-right-width: 0;
|
||||
box-shadow: -1px 1px 1px rgba(0,0,0,0.12);
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
font-size: 1.7rem;
|
||||
top: 50px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 280px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.aboutPH {
|
||||
box-sizing: border-box;
|
||||
color: rgba(255,255,255,0.8);
|
||||
display: block;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.aboutImg {
|
||||
background: url("/admin/img/logo.svg") no-repeat center;
|
||||
background-size: 90px 90px;
|
||||
height: 90px;
|
||||
margin: 0 auto;
|
||||
padding: 2px;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.aboutPH p { margin: 10px 0; }
|
||||
.aboutPH small { display: block; font-size: 1.2rem; }
|
||||
|
||||
.aboutLink {
|
||||
background: #fff;
|
||||
border-top: 1px solid #ddd;
|
||||
display: table;
|
||||
font-size: 1.4rem;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aboutLink a {
|
||||
display: table-cell;
|
||||
padding: 14px;
|
||||
min-width: 50%;
|
||||
}
|
||||
|
||||
main {
|
||||
display: block;
|
||||
width: 80%;
|
||||
padding: 10px;
|
||||
font-size: 1em;
|
||||
background-color: rgba(255,255,255,0.85);
|
||||
margin: 8px auto;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(0,0,0,0.25);
|
||||
box-shadow: 4px 4px rgba(0,0,0,0.1);
|
||||
line-height: 1.2em;
|
||||
border-radius: 8px;
|
||||
background: #ecf0f5;
|
||||
font-size: 1.65rem;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
h2 { /* Rgba is shared with .transparent th */
|
||||
font: 1.15em sans-serif;
|
||||
background-color: rgba(255,0,0,0.4);
|
||||
text-shadow: none;
|
||||
line-height: 1.1em;
|
||||
padding-bottom: 1px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
background: -webkit-linear-gradient(left, rgba(0,0,0,0.25), transparent 80%) no-repeat;
|
||||
background: linear-gradient(to right, rgba(0,0,0,0.25), transparent 80%) no-repeat;
|
||||
background-size: 100% 1px;
|
||||
background-position: 0 17px;
|
||||
#bpOutput {
|
||||
background: #00c0ef;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(0,0,0,0.1);
|
||||
color: #fff;
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
h2:first-child { margin-top: 0; }
|
||||
h2 ~ *:not(h2) { margin-left: 4px; }
|
||||
li { padding: 2px 0; }
|
||||
li::before { content: "\00BB\00a0"; }
|
||||
li a { position: relative; top: 1px; } /* Center bullet-point arrows */
|
||||
|
||||
/* Button Style */
|
||||
.buttons a, button, input, .transparent th a { /* Swapped rgba is shared with input[type='url'] */
|
||||
display: inline-block;
|
||||
color: rgba(32,32,32,0.9);
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
text-shadow: 0 1px rgba(255,255,255,0.2);
|
||||
line-height: 0.86em;
|
||||
font-size: 1em;
|
||||
padding: 4px 8px;
|
||||
background: #FAFAFA;
|
||||
background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.05), rgba(0,0,0,0.05));
|
||||
background-image: linear-gradient(to bottom, rgba(255,255,255,0.05), rgba(0,0,0,0.05));
|
||||
border: 1px solid rgba(0,0,0,0.25);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 0 rgba(0,0,0,0.04);
|
||||
#bpOutput:before {
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='14' viewBox='0 0 7 14'%3E%3Cpath fill='%23fff' d='M6,11a1.371,1.371,0,0,1,1,1v1a1.371,1.371,0,0,1-1,1H1a1.371,1.371,0,0,1-1-1V12a1.371,1.371,0,0,1,1-1H2V8H1A1.371,1.371,0,0,1,0,7V6A1.371,1.371,0,0,1,1,5H4A1.371,1.371,0,0,1,5,6v5H6ZM3.5,0A1.5,1.5,0,1,1,2,1.5,1.5,1.5,0,0,1,3.5,0Z'/%3E%3C/svg%3E") no-repeat center left;
|
||||
display: block;
|
||||
font-size: 1.8rem;
|
||||
text-indent: 15px;
|
||||
}
|
||||
|
||||
.buttons { white-space: nowrap; width: 100%; display: table; }
|
||||
.buttons33 { white-space: nowrap; width: 33.333%; display: table; text-align: center; margin-left: 33.333% }
|
||||
.mini a { width: 50%; }
|
||||
a.safe { background-color: rgba(0,220,0,0.5); }
|
||||
button.safe { background-color: rgba(0,220,0,0.5); }
|
||||
a.warn { background-color: rgba(220,0,0,0.5); }
|
||||
#bpOutput.hidden { display: none; }
|
||||
#bpOutput.success { background: #00a65a; }
|
||||
#bpOutput.error { background: #dd4b39; }
|
||||
|
||||
.blocked a, .mini a { display: table-cell; }
|
||||
.blocked a.safe50 { width: 50%; background-color: rgba(0,220,0,0.5); }
|
||||
.blocked a.safe33 { width: 33.333%; background-color: rgba(0,220,0,0.5); }
|
||||
.blockMsg, .flagMsg {
|
||||
font: bold 1.8rem Consolas, Courier, monospace;
|
||||
padding: 5px 10px 10px 10px;
|
||||
text-indent: 15px;
|
||||
}
|
||||
|
||||
/* Types of text */
|
||||
.msg { white-space: pre; overflow: auto; -webkit-overflow-scrolling: touch; display: block; line-height: 1.2em; font-weight: bold; font-size: 1.15em; margin: 4px 8px 8px 8px; white-space: pre-line; }
|
||||
#bpHelpTxt { padding-bottom: 10px; }
|
||||
|
||||
footer { font-size: 0.8em; text-align: center; width: 87%; margin: 4px auto; }
|
||||
.buttons {
|
||||
border-spacing: 5px 0;
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.buttons * {
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(0,0,0,0.1);
|
||||
box-sizing: content-box;
|
||||
display: table-cell;
|
||||
font-size: 1.65rem;
|
||||
margin-right: 5px;
|
||||
min-height: 20px;
|
||||
padding: 6px 12px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.buttons a:hover { text-decoration: none; }
|
||||
|
||||
/* Button hover dark overlay */
|
||||
.buttons *:not(input):not([disabled]):hover {
|
||||
background-image: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.1));
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
/* Button active shadow inset */
|
||||
.buttons *:not([disabled]):not(input):active {
|
||||
box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);
|
||||
}
|
||||
|
||||
/* Input border colour */
|
||||
.buttons *:not([disabled]):hover, .buttons input:focus {
|
||||
border-color: rgba(0,0,0,0.25);
|
||||
}
|
||||
|
||||
#bpButtons * { width: 50%; color: #FFF; }
|
||||
#bpBack { background-color: #00a65a; }
|
||||
#bpInfo { background-color: #3c8dbc; }
|
||||
#bpWhitelist { background-color: #dd4b39; }
|
||||
|
||||
#blockpage .buttons [type=password][disabled] { color: rgba(0,0,0,1); }
|
||||
#blockpage .buttons [disabled] { color: rgba(0,0,0,0.55); background-color: #e3e3e3; }
|
||||
#blockpage .buttons [type=password]:-ms-input-placeholder { color: rgba(51,51,51,0.8); }
|
||||
|
||||
input[type=password] { font-size: 1.5rem; }
|
||||
|
||||
@keyframes slidein { from { max-height: 0; opacity: 0; } to { max-height: 300px; opacity: 1; } }
|
||||
#bpMoreToggle:checked ~ #bpMoreInfo { display: block; margin-top: 8px; animation: slidein 0.05s linear; }
|
||||
#bpMoreInfo { display: none; margin-top: 10px; }
|
||||
|
||||
#bpQueryOutput {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.65rem;
|
||||
margin: 5px 0 0 0;
|
||||
overflow: auto;
|
||||
padding: 0 5px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
#bpQueryOutput span { margin-right: 4px; }
|
||||
|
||||
#bpWLButtons { width: auto; margin-top: 10px; }
|
||||
#bpWLButtons * { display: inline-block; }
|
||||
#bpWLDomain { display: none; }
|
||||
#bpWLPassword { width: 160px; }
|
||||
#bpWhitelist { color: #fff; }
|
||||
|
||||
footer {
|
||||
background: #fff;
|
||||
border-top: 1px solid #d2d6de;
|
||||
color: #444;
|
||||
font: 1.2rem Consolas, Courier, monospace;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* Responsive Content */
|
||||
@media only screen and (max-width: 500px) {
|
||||
h1 a { font-size: 1.8rem; min-width: 170px; }
|
||||
footer span:before { content: "Generated "; }
|
||||
footer span { display: block; }
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1251px) {
|
||||
#bpWrapper, footer { border-radius: 0 0 5px 5px; }
|
||||
#bpAbout { border-right-width: 1px; }
|
||||
}
|
||||
|
@@ -1 +0,0 @@
|
||||
var x = "Pi-hole: A black hole for Internet advertisements."
|
@@ -1,224 +1,359 @@
|
||||
<?php
|
||||
/* Detailed Pi-hole Block Page: Show "Website Blocked" if user browses to site, but not to image/file requests based on the work of WaLLy3K for DietPi & Pi-Hole */
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
function validIP($address){
|
||||
if (preg_match('/[.:0]/', $address) && !preg_match('/[1-9a-f]/', $address)) {
|
||||
// Test if address contains either `:` or `0` but not 1-9 or a-f
|
||||
return false;
|
||||
}
|
||||
return !filter_var($address, FILTER_VALIDATE_IP) === false;
|
||||
}
|
||||
// Sanitise HTTP_HOST output
|
||||
$serverName = htmlspecialchars($_SERVER["HTTP_HOST"]);
|
||||
// Remove external ipv6 brackets if any
|
||||
$serverName = preg_replace('/^\[(.*)\]$/', '${1}', $serverName);
|
||||
|
||||
$uri = escapeshellcmd($_SERVER['REQUEST_URI']);
|
||||
$serverName = escapeshellcmd($_SERVER['SERVER_NAME']);
|
||||
if (!is_file("/etc/pihole/setupVars.conf"))
|
||||
die("[ERROR] File not found: <code>/etc/pihole/setupVars.conf</code>");
|
||||
|
||||
// If the server name is 'pi.hole', it's likely a user trying to get to the admin panel.
|
||||
// Let's be nice and redirect them.
|
||||
if ($serverName === 'pi.hole')
|
||||
{
|
||||
header('HTTP/1.1 301 Moved Permanently');
|
||||
header("Location: /admin/");
|
||||
}
|
||||
|
||||
// Retrieve server URI extension (EG: jpg, exe, php)
|
||||
ini_set('pcre.recursion_limit',100);
|
||||
$uriExt = pathinfo($uri, PATHINFO_EXTENSION);
|
||||
|
||||
// Define which URL extensions get rendered as "Website Blocked"
|
||||
$webExt = array('asp', 'htm', 'html', 'php', 'rss', 'xml');
|
||||
|
||||
// Get IPv4 and IPv6 addresses from setupVars.conf (if available)
|
||||
// Get values from setupVars.conf
|
||||
$setupVars = parse_ini_file("/etc/pihole/setupVars.conf");
|
||||
$ipv4 = isset($setupVars["IPV4_ADDRESS"]) ? explode("/", $setupVars["IPV4_ADDRESS"])[0] : $_SERVER['SERVER_ADDR'];
|
||||
$ipv6 = isset($setupVars["IPV6_ADDRESS"]) ? explode("/", $setupVars["IPV6_ADDRESS"])[0] : $_SERVER['SERVER_ADDR'];
|
||||
$svPasswd = !empty($setupVars["WEBPASSWORD"]);
|
||||
$svEmail = (!empty($setupVars["ADMIN_EMAIL"]) && filter_var($setupVars["ADMIN_EMAIL"], FILTER_VALIDATE_EMAIL)) ? $setupVars["ADMIN_EMAIL"] : "";
|
||||
unset($setupVars);
|
||||
|
||||
$AUTHORIZED_HOSTNAMES = array(
|
||||
$ipv4,
|
||||
$ipv6,
|
||||
str_replace(array("[","]"), array("",""), $_SERVER["SERVER_ADDR"]),
|
||||
"pi.hole",
|
||||
"localhost");
|
||||
// Allow user set virtual hostnames
|
||||
$virtual_host = getenv('VIRTUAL_HOST');
|
||||
if (!empty($virtual_host))
|
||||
array_push($AUTHORIZED_HOSTNAMES, $virtual_host);
|
||||
// Set landing page location, found within /var/www/html/
|
||||
$landPage = "../landing.php";
|
||||
|
||||
// Immediately quit since we didn't block this page (the IP address or pi.hole is explicitly requested)
|
||||
if(validIP($serverName) || in_array($serverName,$AUTHORIZED_HOSTNAMES))
|
||||
{
|
||||
http_response_code(404);
|
||||
die();
|
||||
// Define array for hostnames to be accepted as self address for splash page
|
||||
$authorizedHosts = [];
|
||||
if (!empty($_SERVER["FQDN"])) {
|
||||
// If setenv.add-environment = ("fqdn" => "true") is configured in lighttpd,
|
||||
// append $serverName to $authorizedHosts
|
||||
array_push($authorizedHosts, $serverName);
|
||||
} else if (!empty($_SERVER["VIRTUAL_HOST"])) {
|
||||
// Append virtual hostname to $authorizedHosts
|
||||
array_push($authorizedHosts, $_SERVER["VIRTUAL_HOST"]);
|
||||
}
|
||||
|
||||
if(in_array($uriExt, $webExt) || empty($uriExt))
|
||||
{
|
||||
// Requested resource has an extension listed in $webExt
|
||||
// or no extension (index access to some folder incl. the root dir)
|
||||
$showPage = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Something else
|
||||
$showPage = false;
|
||||
// Set which extension types render as Block Page (Including "" for index.ext)
|
||||
$validExtTypes = array("asp", "htm", "html", "php", "rss", "xml", "");
|
||||
|
||||
// Get extension of current URL
|
||||
$currentUrlExt = pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION);
|
||||
|
||||
// Check if this is served over HTTP or HTTPS
|
||||
if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") {
|
||||
$proto = "https";
|
||||
} else {
|
||||
$proto = "http";
|
||||
}
|
||||
|
||||
// Handle incoming URI types
|
||||
if (!$showPage)
|
||||
{
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<script>window.close();</script></head>
|
||||
<body>
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
die();
|
||||
// Set mobile friendly viewport
|
||||
$viewPort = '<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>';
|
||||
|
||||
// Set response header
|
||||
function setHeader($type = "x") {
|
||||
header("X-Pi-hole: A black hole for Internet advertisements.");
|
||||
if (isset($type) && $type === "js") header("Content-Type: application/javascript");
|
||||
}
|
||||
|
||||
// Get Pi-hole version
|
||||
$piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0');
|
||||
// Determine block page type
|
||||
if ($serverName === "pi.hole") {
|
||||
// Redirect to Web Interface
|
||||
exit(header("Location: /admin"));
|
||||
} elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) {
|
||||
// Set Splash Page output
|
||||
$splashPage = "
|
||||
<html><head>
|
||||
$viewPort
|
||||
<link rel='stylesheet' href='/pihole/blockingpage.css' type='text/css'/>
|
||||
</head><body id='splashpage'><img src='/admin/img/logo.svg'/><br/>Pi-<b>hole</b>: Your black hole for Internet advertisements<br><a href='/admin'>Did you mean to go to the admin panel?</a></body></html>
|
||||
";
|
||||
|
||||
// Don't show the URI if it is the root directory
|
||||
if($uri == "/")
|
||||
{
|
||||
$uri = "";
|
||||
// Set splash/landing page based off presence of $landPage
|
||||
$renderPage = is_file(getcwd()."/$landPage") ? include $landPage : "$splashPage";
|
||||
|
||||
// Unset variables so as to not be included in $landPage
|
||||
unset($serverName, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort);
|
||||
|
||||
// Render splash/landing page when directly browsing via IP or authorised hostname
|
||||
exit($renderPage);
|
||||
} elseif ($currentUrlExt === "js") {
|
||||
// Serve Pi-hole Javascript for blocked domains requesting JS
|
||||
exit(setHeader("js").'var x = "Pi-hole: A black hole for Internet advertisements."');
|
||||
} elseif (strpos($_SERVER["REQUEST_URI"], "?") !== FALSE && isset($_SERVER["HTTP_REFERER"])) {
|
||||
// Serve blank image upon receiving REQUEST_URI w/ query string & HTTP_REFERRER
|
||||
// e.g: An iframe of a blocked domain
|
||||
exit(setHeader().'<html>
|
||||
<head><script>window.close();</script></head>
|
||||
<body><img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="></body>
|
||||
</html>');
|
||||
} elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) {
|
||||
// Serve SVG upon receiving non $validExtTypes URL extension or query string
|
||||
// e.g: Not an iframe of a blocked domain, such as when browsing to a file/query directly
|
||||
// QoL addition: Allow the SVG to be clicked on in order to quickly show the full Block Page
|
||||
$blockImg = '<a href="/"><svg xmlns="http://www.w3.org/2000/svg" width="110" height="16"><defs><style>a {text-decoration: none;} circle {stroke: rgba(152,2,2,0.5); fill: none; stroke-width: 2;} rect {fill: rgba(152,2,2,0.5);} text {opacity: 0.3; font: 11px Arial;}</style></defs><circle cx="8" cy="8" r="7"/><rect x="10.3" y="-6" width="2" height="12" transform="rotate(45)"/><text x="19.3" y="12">Blocked by Pi-hole</text></svg></a>';
|
||||
exit(setHeader()."<html>
|
||||
<head>$viewPort</head>
|
||||
<body>$blockImg</body>
|
||||
</html>");
|
||||
}
|
||||
|
||||
/* Start processing Block Page from here */
|
||||
|
||||
// Define admin email address text based off $svEmail presence
|
||||
$bpAskAdmin = !empty($svEmail) ? '<a href="mailto:'.$svEmail.'?subject=Site Blocked: '.$serverName.'"></a>' : "<span/>";
|
||||
|
||||
// Determine if at least one block list has been generated
|
||||
$blocklistglob = glob("/etc/pihole/list.0.*.domains");
|
||||
if ($blocklistglob === array()) {
|
||||
die("[ERROR] There are no domain lists generated lists within <code>/etc/pihole/</code>! Please update gravity by running <code>pihole -g</code>, or repair Pi-hole using <code>pihole -r</code>.");
|
||||
}
|
||||
|
||||
// Set location of adlists file
|
||||
if (is_file("/etc/pihole/adlists.list")) {
|
||||
$adLists = "/etc/pihole/adlists.list";
|
||||
} elseif (is_file("/etc/pihole/adlists.default")) {
|
||||
$adLists = "/etc/pihole/adlists.default";
|
||||
} else {
|
||||
die("[ERROR] File not found: <code>/etc/pihole/adlists.list</code>");
|
||||
}
|
||||
|
||||
// Get all URLs starting with "http" or "www" from adlists and re-index array numerically
|
||||
$adlistsUrls = array_values(preg_grep("/(^http)|(^www)/i", file($adLists, FILE_IGNORE_NEW_LINES)));
|
||||
|
||||
if (empty($adlistsUrls))
|
||||
die("[ERROR]: There are no adlist URL's found within <code>$adLists</code>");
|
||||
|
||||
// Get total number of blocklists (Including Whitelist, Blacklist & Wildcard lists)
|
||||
$adlistsCount = count($adlistsUrls) + 3;
|
||||
|
||||
// Set query timeout
|
||||
ini_set("default_socket_timeout", 3);
|
||||
|
||||
// Logic for querying blocklists
|
||||
function queryAds($serverName) {
|
||||
// Determine the time it takes while querying adlists
|
||||
$preQueryTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
|
||||
$queryAds = file("http://127.0.0.1/admin/scripts/pi-hole/php/queryads.php?domain=$serverName&bp", FILE_IGNORE_NEW_LINES);
|
||||
$queryAds = array_values(array_filter(preg_replace("/data:\s+/", "", $queryAds)));
|
||||
$queryTime = sprintf("%.0f", (microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]) - $preQueryTime);
|
||||
|
||||
// Exception Handling
|
||||
try {
|
||||
// Define Exceptions
|
||||
if (strpos($queryAds[0], "No exact results") !== FALSE) {
|
||||
// Return "none" into $queryAds array
|
||||
return array("0" => "none");
|
||||
} else if ($queryTime >= ini_get("default_socket_timeout")) {
|
||||
// Connection Timeout
|
||||
throw new Exception ("Connection timeout (".ini_get("default_socket_timeout")."s)");
|
||||
} elseif (!strpos($queryAds[0], ".") !== false) {
|
||||
// Unknown $queryAds output
|
||||
throw new Exception ("Unhandled error message (<code>$queryAds[0]</code>)");
|
||||
}
|
||||
return $queryAds;
|
||||
} catch (Exception $e) {
|
||||
// Return exception as array
|
||||
return array("0" => "error", "1" => $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Get results of queryads.php exact search
|
||||
$queryAds = queryAds($serverName);
|
||||
|
||||
// Pass error through to Block Page
|
||||
if ($queryAds[0] === "error")
|
||||
die("[ERROR]: Unable to parse results from <i>queryads.php</i>: <code>".$queryAds[1]."</code>");
|
||||
|
||||
// Count total number of matching blocklists
|
||||
$featuredTotal = count($queryAds);
|
||||
|
||||
// Place results into key => value array
|
||||
$queryResults = null;
|
||||
foreach ($queryAds as $str) {
|
||||
$value = explode(" ", $str);
|
||||
@$queryResults[$value[0]] .= "$value[1]";
|
||||
}
|
||||
|
||||
// Determine if domain has been blacklisted, whitelisted, wildcarded or CNAME blocked
|
||||
if (strpos($queryAds[0], "blacklist") !== FALSE) {
|
||||
$notableFlagClass = "blacklist";
|
||||
$adlistsUrls = array("π" => substr($queryAds[0], 2));
|
||||
} elseif (strpos($queryAds[0], "whitelist") !== FALSE) {
|
||||
$notableFlagClass = "noblock";
|
||||
$adlistsUrls = array("π" => substr($queryAds[0], 2));
|
||||
$wlInfo = "recentwl";
|
||||
} elseif (strpos($queryAds[0], "wildcard") !== FALSE) {
|
||||
$notableFlagClass = "wildcard";
|
||||
$adlistsUrls = array("π" => substr($queryAds[0], 2));
|
||||
} elseif ($queryAds[0] === "none") {
|
||||
$featuredTotal = "0";
|
||||
$notableFlagClass = "noblock";
|
||||
|
||||
// QoL addition: Determine appropriate info message if CNAME exists
|
||||
// Suggests to the user that $serverName has a CNAME (alias) that may be blocked
|
||||
$dnsRecord = dns_get_record("$serverName")[0];
|
||||
if (array_key_exists("target", $dnsRecord)) {
|
||||
$wlInfo = $dnsRecord['target'];
|
||||
} else {
|
||||
$wlInfo = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Set #bpOutput notification
|
||||
$wlOutputClass = (isset($wlInfo) && $wlInfo === "recentwl") ? $wlInfo : "hidden";
|
||||
$wlOutput = (isset($wlInfo) && $wlInfo !== "recentwl") ? "<a href='http://$wlInfo'>$wlInfo</a>" : "";
|
||||
|
||||
// Get Pi-hole Core version
|
||||
$phVersion = exec("cd /etc/.pihole/ && git describe --long --tags");
|
||||
|
||||
// Print $execTime on development branches
|
||||
// Testing for - is marginally faster than "git rev-parse --abbrev-ref HEAD"
|
||||
if (explode("-", $phVersion)[1] != "0")
|
||||
$execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
|
||||
|
||||
// Please Note: Text is added via CSS to allow an admin to provide a localised
|
||||
// language without the need to edit this file
|
||||
|
||||
setHeader();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<!-- Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL. -->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='UTF-8'/>
|
||||
<title>Website Blocked</title>
|
||||
<link rel='stylesheet' href='http://pi.hole/pihole/blockingpage.css'/>
|
||||
<link rel='shortcut icon' href='http://pi.hole/admin/img/favicon.png' type='image/png'/>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1.0,maximum-scale=1.0, user-scalable=no'/>
|
||||
<meta name='robots' content='noindex,nofollow'/>
|
||||
<meta charset="UTF-8">
|
||||
<?=$viewPort ?>
|
||||
<meta name="robots" content="noindex,nofollow"/>
|
||||
<meta http-equiv="x-dns-prefetch-control" content="off">
|
||||
<link rel="shortcut icon" href="<?=$proto ?>://pi.hole/admin/img/favicon.png" type="image/x-icon"/>
|
||||
<link rel="stylesheet" href="<?=$proto ?>://pi.hole/pihole/blockingpage.css" type="text/css"/>
|
||||
<title>● <?=$serverName ?></title>
|
||||
<script src="<?=$proto ?>://pi.hole/admin/scripts/vendor/jquery.min.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
<?php
|
||||
// Remove href fallback from "Back to safety" button
|
||||
if ($featuredTotal > 0) {
|
||||
echo '$("#bpBack").removeAttr("href");';
|
||||
|
||||
// Enable whitelisting if JS is available
|
||||
echo '$("#bpWhitelist").prop("disabled", false);';
|
||||
|
||||
// Enable password input if necessary
|
||||
if (!empty($svPasswd)) {
|
||||
echo '$("#bpWLPassword").attr("placeholder", "Password");';
|
||||
echo '$("#bpWLPassword").prop("disabled", false);';
|
||||
}
|
||||
// Otherwise hide the input
|
||||
else {
|
||||
echo '$("#bpWLPassword").hide();';
|
||||
}
|
||||
}
|
||||
?>
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body id="body">
|
||||
<body id="blockpage"><div id="bpWrapper">
|
||||
<header>
|
||||
<h1><a href='/'>Website Blocked</a></h1>
|
||||
<h1 id="bpTitle">
|
||||
<a class="title" href="/"><?php //Website Blocked ?></a>
|
||||
</h1>
|
||||
<div class="spc"></div>
|
||||
|
||||
<input id="bpAboutToggle" type="checkbox"/>
|
||||
<div id="bpAbout">
|
||||
<div class="aboutPH">
|
||||
<div class="aboutImg"/></div>
|
||||
<p>Open Source Ad Blocker
|
||||
<small>Designed for Raspberry Pi</small>
|
||||
</p>
|
||||
</div>
|
||||
<div class="aboutLink">
|
||||
<a class="linkPH" href="https://github.com/pi-hole/pi-hole/wiki/What-is-Pi-hole%3F-A-simple-explanation"><?php //About PH ?></a>
|
||||
<?php if (!empty($svEmail)) echo '<a class="linkEmail" href="mailto:'.$svEmail.'"></a>'; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bpAlt">
|
||||
<label class="altBtn" for="bpAboutToggle"><?php //Why am I here? ?></label>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div>Access to the following site has been blocked:<br/>
|
||||
<span class='pre msg'><?php echo $serverName.$uri; ?></span></div>
|
||||
<div>If you have an ongoing use for this website, please ask the owner of the Pi-hole in your network to have it whitelisted.</div>
|
||||
<input id="domain" type="hidden" value="<?php echo $serverName; ?>">
|
||||
<input id="quiet" type="hidden" value="yes">
|
||||
<button id="btnSearch" class="buttons blocked" type="button" style="visibility: hidden;"></button>
|
||||
This page is blocked because it is explicitly contained within the following block list(s):
|
||||
<pre id="output" style="width: 100%; height: 100%;" hidden="true"></pre><br/>
|
||||
<div class='buttons blocked'>
|
||||
<a class='safe33' href='javascript:history.back()'>Go back</a>
|
||||
<a class='safe33' id="whitelisting">Whitelist this page</a>
|
||||
<a class='safe33' href='javascript:window.close()'>Close window</a>
|
||||
</div>
|
||||
<div style="width: 98%; text-align: center; padding: 10px;" hidden="true" id="whitelistingform">
|
||||
<p>Note that whitelisting domains which are blocked using the wildcard method won't work.</p>
|
||||
<p>Password required!</p><br/>
|
||||
<form>
|
||||
<input name="list" type="hidden" value="white"><br/>
|
||||
Domain:<br/>
|
||||
<input name="domain" value="<?php echo $serverName ?>" disabled><br/><br/>
|
||||
Password:<br/>
|
||||
<input type="password" id="pw" name="pw"><br/><br/>
|
||||
<button class="buttons33 safe" id="btnAdd" type="button">Whitelist</button>
|
||||
</form><br/>
|
||||
<pre id="whitelistingoutput" style="width: 100%; height: 100%; padding: 5px;" hidden="true"></pre><br/>
|
||||
</div>
|
||||
<div id="bpOutput" class="<?=$wlOutputClass ?>"><?=$wlOutput ?></div>
|
||||
<div id="bpBlock">
|
||||
<p class="blockMsg"><?=$serverName ?></p>
|
||||
</div>
|
||||
<?php if(isset($notableFlagClass)) { ?>
|
||||
<div id="bpFlag">
|
||||
<p class="flagMsg <?=$notableFlagClass ?>"></p>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div id="bpHelpTxt"><?=$bpAskAdmin ?></div>
|
||||
<div id="bpButtons" class="buttons">
|
||||
<a id="bpBack" onclick="javascript:history.back()" href="about:home"></a>
|
||||
<?php if ($featuredTotal > 0) echo '<label id="bpInfo" for="bpMoreToggle"></label>'; ?>
|
||||
</div>
|
||||
<input id="bpMoreToggle" type="checkbox">
|
||||
<div id="bpMoreInfo">
|
||||
<span id="bpFoundIn"><span><?=$featuredTotal ?></span><?=$adlistsCount ?></span>
|
||||
<pre id='bpQueryOutput'><?php if ($featuredTotal > 0) foreach ($queryResults as $num => $value) { echo "<span>[$num]:</span>$adlistsUrls[$num]\n"; } ?></pre>
|
||||
|
||||
<form id="bpWLButtons" class="buttons">
|
||||
<input id="bpWLDomain" type="text" value="<?=$serverName ?>" disabled/>
|
||||
<input id="bpWLPassword" type="password" placeholder="Javascript disabled" disabled/><button id="bpWhitelist" type="button" disabled></button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
<footer>Generated <?php echo date('D g:i A, M d'); ?> by Pi-hole <?php echo $piHoleVersion; ?></footer>
|
||||
<script src="http://pi.hole/admin/scripts/vendor/jquery.min.js"></script>
|
||||
<script>
|
||||
// Create event for when the output is appended to
|
||||
(function($) {
|
||||
var origAppend = $.fn.append;
|
||||
|
||||
$.fn.append = function () {
|
||||
return origAppend.apply(this, arguments).trigger("append");
|
||||
};
|
||||
})(jQuery);
|
||||
</script>
|
||||
<script src="http://pi.hole/admin/scripts/pi-hole/js/queryads.js"></script>
|
||||
<footer><span><?=date("l g:i A, F dS"); ?>.</span> Pi-hole <?=$phVersion ?> (<?=gethostname()."/".$_SERVER["SERVER_ADDR"]; if (isset($execTime)) printf("/%.2fs", $execTime); ?>)</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function inIframe () {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (e) {
|
||||
return true;
|
||||
function add() {
|
||||
$("#bpOutput").removeClass("hidden error exception");
|
||||
$("#bpOutput").addClass("add");
|
||||
var domain = "<?=$serverName ?>";
|
||||
var pw = $("#bpWLPassword");
|
||||
if(domain.length === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to detect if page is loaded within iframe
|
||||
if(inIframe())
|
||||
{
|
||||
// Within iframe
|
||||
// hide content of page
|
||||
$('#body').hide();
|
||||
// remove background
|
||||
document.body.style.backgroundImage = "none";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Query adlists
|
||||
$( "#btnSearch" ).click();
|
||||
}
|
||||
|
||||
$( "#whitelisting" ).on( "click", function(){ $( "#whitelistingform" ).removeAttr( "hidden" ); });
|
||||
|
||||
// Remove whitelist functionality if the domain was blocked because of a wildcard
|
||||
$( "#output" ).bind("append", function(){
|
||||
if($( "#output" ).contents()[0].data.indexOf("Wildcard blocking") !== -1)
|
||||
{
|
||||
$( "#whitelisting" ).hide();
|
||||
$( "#whitelistingform" ).hide();
|
||||
}
|
||||
});
|
||||
|
||||
function add() {
|
||||
var domain = $("#domain");
|
||||
var pw = $("#pw");
|
||||
if(domain.val().length === 0){
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "/admin/scripts/pi-hole/php/add.php",
|
||||
method: "post",
|
||||
data: {"domain":domain.val(), "list":"white", "pw":pw.val()},
|
||||
success: function(response) {
|
||||
$( "#whitelistingoutput" ).removeAttr( "hidden" );
|
||||
if(response.indexOf("Pi-hole blocking") !== -1)
|
||||
{
|
||||
// Reload page after 5 seconds
|
||||
setTimeout(function(){window.location.reload(1);}, 5000);
|
||||
$( "#whitelistingoutput" ).html("---> Success <---<br/>You may have to flush your DNS cache");
|
||||
}
|
||||
else
|
||||
{
|
||||
$( "#whitelistingoutput" ).html("---> "+response+" <---");
|
||||
}
|
||||
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
$( "#whitelistingoutput" ).removeAttr( "hidden" );
|
||||
$( "#whitelistingoutput" ).html("---> Unknown Error <---");
|
||||
}
|
||||
});
|
||||
}
|
||||
// Handle enter button for adding domains
|
||||
$(document).keypress(function(e) {
|
||||
if(e.which === 13 && $("#pw").is(":focus")) {
|
||||
$.ajax({
|
||||
url: "/admin/scripts/pi-hole/php/add.php",
|
||||
method: "post",
|
||||
data: {"domain":domain, "list":"white", "pw":pw.val()},
|
||||
success: function(response) {
|
||||
if(response.indexOf("Pi-hole blocking") !== -1) {
|
||||
setTimeout(function(){window.location.reload(1);}, 10000);
|
||||
$("#bpOutput").removeClass("add");
|
||||
$("#bpOutput").addClass("success");
|
||||
$("#bpOutput").html("");
|
||||
} else {
|
||||
$("#bpOutput").removeClass("add");
|
||||
$("#bpOutput").addClass("error");
|
||||
$("#bpOutput").html(""+response+"");
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
$("#bpOutput").removeClass("add");
|
||||
$("#bpOutput").addClass("exception");
|
||||
$("#bpOutput").html("");
|
||||
}
|
||||
});
|
||||
}
|
||||
<?php if ($featuredTotal > 0) { ?>
|
||||
$(document).keypress(function(e) {
|
||||
if(e.which === 13 && $("#bpWLPassword").is(":focus")) {
|
||||
add();
|
||||
}
|
||||
});
|
||||
$("#bpWhitelist").on("click", function() {
|
||||
add();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle buttons
|
||||
$("#btnAdd").on("click", function() {
|
||||
add();
|
||||
});
|
||||
});
|
||||
<?php } ?>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</body></html>
|
||||
|
@@ -2,18 +2,16 @@
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# lighttpd config for Pi-hole
|
||||
# Lighttpd config for Pi-hole
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
# FILE AUTOMATICALLY OVERWRITTEN BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
|
||||
# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
|
||||
# #
|
||||
# CHANGES SHOULD BE MADE IN A SEPERATE CONFIG FILE: #
|
||||
# CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE: #
|
||||
# /etc/lighttpd/external.conf #
|
||||
###############################################################################
|
||||
|
||||
@@ -39,9 +37,8 @@ server.port = 80
|
||||
accesslog.filename = "/var/log/lighttpd/access.log"
|
||||
accesslog.format = "%{%s}t|%V|%r|%s|%b"
|
||||
|
||||
|
||||
index-file.names = ( "index.php", "index.html", "index.lighttpd.html" )
|
||||
url.access-deny = ( "~", ".inc" )
|
||||
url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
|
||||
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
|
||||
|
||||
compress.cache-dir = "/var/cache/lighttpd/compress/"
|
||||
@@ -50,32 +47,30 @@ compress.filetype = ( "application/javascript", "text/css", "text/html
|
||||
# default listening port for IPv6 falls back to the IPv4 port
|
||||
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
|
||||
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
|
||||
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
|
||||
|
||||
# Prevent Lighttpd from enabling Let's Encrypt SSL for every blocked domain
|
||||
#include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
|
||||
include_shell "find /etc/lighttpd/conf-enabled -name '*.conf' -a ! -name 'letsencrypt.conf' -printf 'include \"%p\"\n' 2>/dev/null"
|
||||
|
||||
# If the URL starts with /admin, it is the Web interface
|
||||
$HTTP["url"] =~ "^/admin/" {
|
||||
# Create a response header for debugging using curl -I
|
||||
# Create a response header for debugging using curl -I
|
||||
setenv.add-response-header = (
|
||||
"X-Pi-hole" => "The Pi-hole Web interface is working!",
|
||||
"X-Frame-Options" => "DENY"
|
||||
)
|
||||
}
|
||||
|
||||
# Rewite js requests, must be out of $HTTP block due to bug #2526
|
||||
url.rewrite = ( "^(?!/admin/).*\.js$" => "pihole/index.js" )
|
||||
|
||||
# If the URL does not start with /admin, then it is a query for an ad domain
|
||||
$HTTP["url"] =~ "^(?!/admin)/.*" {
|
||||
# Create a response header for debugging using curl -I
|
||||
setenv.add-response-header = ( "X-Pi-hole" => "A black hole for Internet advertisements." )
|
||||
}
|
||||
|
||||
# Entering just "pi.hole" into a browser redirects to "pi.hole/admin/"
|
||||
$HTTP["host"] == "pi.hole" {
|
||||
$HTTP["url"] == "/" {
|
||||
url.redirect = ( "" => "/admin/" )
|
||||
$HTTP["url"] =~ ".ttf$" {
|
||||
# Allow Block Page access to local fonts
|
||||
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
|
||||
}
|
||||
}
|
||||
|
||||
# Block . files from being served, such as .git, .github, .gitignore
|
||||
$HTTP["url"] =~ "^/admin/\.(.*)" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
|
||||
# Add user chosen options held in external file
|
||||
# This uses include_shell instead of an include wildcard for compatibility
|
||||
include_shell "cat external.conf 2>/dev/null"
|
||||
|
@@ -7,13 +7,11 @@
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
# FILE AUTOMATICALLY OVERWRITTEN BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
|
||||
# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
|
||||
# #
|
||||
# CHANGES SHOULD BE MADE IN A SEPERATE CONFIG FILE: #
|
||||
# CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE: #
|
||||
# /etc/lighttpd/external.conf #
|
||||
###############################################################################
|
||||
|
||||
@@ -42,7 +40,7 @@ accesslog.format = "%{%s}t|%V|%r|%s|%b"
|
||||
|
||||
|
||||
index-file.names = ( "index.php", "index.html", "index.lighttpd.html" )
|
||||
url.access-deny = ( "~", ".inc" )
|
||||
url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
|
||||
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
|
||||
|
||||
compress.cache-dir = "/var/cache/lighttpd/compress/"
|
||||
@@ -74,25 +72,23 @@ fastcgi.server = ( ".php" =>
|
||||
|
||||
# If the URL starts with /admin, it is the Web interface
|
||||
$HTTP["url"] =~ "^/admin/" {
|
||||
# Create a response header for debugging using curl -I
|
||||
setenv.add-response-header = ( "X-Pi-hole" => "The Pi-hole Web interface is working!" )
|
||||
}
|
||||
# Create a response header for debugging using curl -I
|
||||
setenv.add-response-header = (
|
||||
"X-Pi-hole" => "The Pi-hole Web interface is working!",
|
||||
"X-Frame-Options" => "DENY"
|
||||
)
|
||||
|
||||
# Rewite js requests, must be out of $HTTP block due to bug #2526
|
||||
url.rewrite = ( "^(?!/admin/).*\.js$" => "pihole/index.js" )
|
||||
|
||||
# If the URL does not start with /admin, then it is a query for an ad domain
|
||||
$HTTP["url"] =~ "^(?!/admin)/.*" {
|
||||
# Create a response header for debugging using curl -I
|
||||
setenv.add-response-header = ( "X-Pi-hole" => "A black hole for Internet advertisements." )
|
||||
}
|
||||
|
||||
# Entering just "pi.hole" into a browser redirects to "pi.hole/admin/"
|
||||
$HTTP["host"] == "pi.hole" {
|
||||
$HTTP["url"] == "/" {
|
||||
url.redirect = ( "" => "/admin/" )
|
||||
$HTTP["url"] =~ ".ttf$" {
|
||||
# Allow Block Page access to local fonts
|
||||
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
|
||||
}
|
||||
}
|
||||
|
||||
# Block . files from being served, such as .git, .github, .gitignore
|
||||
$HTTP["url"] =~ "^/admin/\.(.*)" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
|
||||
# Add user chosen options held in external file
|
||||
# This uses include_shell instead of an include wildcard for compatibility
|
||||
include_shell "cat external.conf 2>/dev/null"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,174 +8,210 @@
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
source "/opt/pihole/COL_TABLE"
|
||||
|
||||
while true; do
|
||||
read -rp " ${QST} Are you sure you would like to remove ${COL_WHITE}Pi-hole${COL_NC}? [y/N] " yn
|
||||
case ${yn} in
|
||||
[Yy]* ) break;;
|
||||
[Nn]* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;;
|
||||
* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Must be root to uninstall
|
||||
str="Root user check"
|
||||
if [[ ${EUID} -eq 0 ]]; then
|
||||
echo "::: You are root."
|
||||
echo -e " ${TICK} ${str}"
|
||||
else
|
||||
echo "::: Sudo will be used for the uninstall."
|
||||
# Check if it is actually installed
|
||||
# If it isn't, exit because the unnstall cannot complete
|
||||
if [ -x "$(command -v sudo)" ]; then
|
||||
export SUDO="sudo"
|
||||
else
|
||||
echo "::: Please install sudo or run this as root."
|
||||
exit 1
|
||||
fi
|
||||
# Check if sudo is actually installed
|
||||
# If it isn't, exit because the uninstall can not complete
|
||||
if [ -x "$(command -v sudo)" ]; then
|
||||
export SUDO="sudo"
|
||||
else
|
||||
echo -e " ${CROSS} ${str}
|
||||
Script called with non-root privileges
|
||||
The Pi-hole requires elevated privleges to uninstall"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
readonly PI_HOLE_FILES_DIR="/etc/.pihole"
|
||||
PH_TEST="true"
|
||||
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
|
||||
# setupVars set in basic-install.sh
|
||||
source "${setupVars}"
|
||||
|
||||
# distro_check() sourced from basic-install.sh
|
||||
distro_check
|
||||
|
||||
# Install packages used by the Pi-hole
|
||||
DEPS=("${INSTALLER_DEPS[@]}" "${PIHOLE_DEPS[@]}")
|
||||
if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
|
||||
# Install the Web dependencies
|
||||
DEPS+=("${PIHOLE_WEB_DEPS[@]}")
|
||||
fi
|
||||
|
||||
# Compatability
|
||||
if [ -x "$(command -v rpm)" ]; then
|
||||
# Fedora Family
|
||||
if [ -x "$(command -v dnf)" ]; then
|
||||
PKG_MANAGER="dnf"
|
||||
else
|
||||
PKG_MANAGER="yum"
|
||||
fi
|
||||
PKG_REMOVE="${PKG_MANAGER} remove -y"
|
||||
PIHOLE_DEPS=( bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common git curl unzip wget findutils )
|
||||
package_check() {
|
||||
rpm -qa | grep ^$1- > /dev/null
|
||||
}
|
||||
package_cleanup() {
|
||||
${SUDO} ${PKG_MANAGER} -y autoremove
|
||||
}
|
||||
elif [ -x "$(command -v apt-get)" ]; then
|
||||
# Debian Family
|
||||
PKG_MANAGER="apt-get"
|
||||
PKG_REMOVE="${PKG_MANAGER} -y remove --purge"
|
||||
PIHOLE_DEPS=( dnsutils bc dnsmasq lighttpd php5-common git curl unzip wget )
|
||||
package_check() {
|
||||
dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -c "ok installed"
|
||||
}
|
||||
package_cleanup() {
|
||||
${SUDO} ${PKG_MANAGER} -y autoremove
|
||||
${SUDO} ${PKG_MANAGER} -y autoclean
|
||||
}
|
||||
if [ -x "$(command -v apt-get)" ]; then
|
||||
# Debian Family
|
||||
PKG_REMOVE="${PKG_MANAGER} -y remove --purge"
|
||||
package_check() {
|
||||
dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -c "ok installed"
|
||||
}
|
||||
elif [ -x "$(command -v rpm)" ]; then
|
||||
# Fedora Family
|
||||
PKG_REMOVE="${PKG_MANAGER} remove -y"
|
||||
package_check() {
|
||||
rpm -qa | grep "^$1-" > /dev/null
|
||||
}
|
||||
else
|
||||
echo "OS distribution not supported"
|
||||
exit
|
||||
echo -e " ${CROSS} OS distribution not supported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
spinner() {
|
||||
local pid=$1
|
||||
local delay=0.50
|
||||
local spinstr='/-\|'
|
||||
while [ "$(ps a | awk '{print $1}' | grep "${pid}")" ]; do
|
||||
local temp=${spinstr#?}
|
||||
printf " [%c] " "${spinstr}"
|
||||
local spinstr=${temp}${spinstr%"$temp}"}
|
||||
sleep ${delay}
|
||||
printf "\b\b\b\b\b\b"
|
||||
done
|
||||
printf " \b\b\b\b"
|
||||
}
|
||||
|
||||
removeAndPurge() {
|
||||
# Purge dependencies
|
||||
echo ":::"
|
||||
for i in "${PIHOLE_DEPS[@]}"; do
|
||||
package_check ${i} > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
while true; do
|
||||
read -rp "::: Do you wish to remove ${i} from your system? [y/n]: " yn
|
||||
case ${yn} in
|
||||
[Yy]* ) printf ":::\tRemoving %s..." "${i}"; ${SUDO} ${PKG_REMOVE} "${i}" &> /dev/null & spinner $!; printf "done!\n"; break;;
|
||||
[Nn]* ) printf ":::\tSkipping %s\n" "${i}"; break;;
|
||||
* ) printf "::: You must answer yes or no!\n";;
|
||||
esac
|
||||
done
|
||||
else
|
||||
printf ":::\tPackage %s not installed... Not removing.\n" "${i}"
|
||||
fi
|
||||
done
|
||||
# Purge dependencies
|
||||
echo ""
|
||||
for i in "${DEPS[@]}"; do
|
||||
if package_check "${i}" > /dev/null; then
|
||||
while true; do
|
||||
read -rp " ${QST} Do you wish to remove ${COL_WHITE}${i}${COL_NC} from your system? [Y/N] " yn
|
||||
case ${yn} in
|
||||
[Yy]* )
|
||||
echo -ne " ${INFO} Removing ${i}...";
|
||||
${SUDO} "${PKG_REMOVE} ${i}" &> /dev/null;
|
||||
echo -e "${OVER} ${INFO} Removed ${i}";
|
||||
break;;
|
||||
[Nn]* ) echo -e " ${INFO} Skipped ${i}"; break;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
echo -e " ${INFO} Package ${i} not installed"
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove dependency config files
|
||||
echo "::: Removing dnsmasq config files..."
|
||||
${SUDO} rm /etc/dnsmasq.conf /etc/dnsmasq.conf.orig /etc/dnsmasq.d/01-pihole.conf &> /dev/null
|
||||
# Remove dnsmasq config files
|
||||
${SUDO} rm -f /etc/dnsmasq.conf /etc/dnsmasq.conf.orig /etc/dnsmasq.d/*-pihole*.conf &> /dev/null
|
||||
echo -e " ${TICK} Removing dnsmasq config files"
|
||||
|
||||
# Take care of any additional package cleaning
|
||||
printf "::: Auto removing & cleaning remaining dependencies..."
|
||||
package_cleanup &> /dev/null & spinner $!; printf "done!\n";
|
||||
|
||||
# Call removeNoPurge to remove PiHole specific files
|
||||
removeNoPurge
|
||||
# Call removeNoPurge to remove Pi-hole specific files
|
||||
removeNoPurge
|
||||
}
|
||||
|
||||
removeNoPurge() {
|
||||
echo ":::"
|
||||
# Only web directories/files that are created by pihole should be removed.
|
||||
echo "::: Removing the Pi-hole Web server files..."
|
||||
${SUDO} rm -rf /var/www/html/admin &> /dev/null
|
||||
${SUDO} rm -rf /var/www/html/pihole &> /dev/null
|
||||
${SUDO} rm /var/www/html/index.lighttpd.orig &> /dev/null
|
||||
# Only web directories/files that are created by Pi-hole should be removed
|
||||
echo -ne " ${INFO} Removing Web Interface..."
|
||||
${SUDO} rm -rf /var/www/html/admin &> /dev/null
|
||||
${SUDO} rm -rf /var/www/html/pihole &> /dev/null
|
||||
${SUDO} rm -f /var/www/html/index.lighttpd.orig &> /dev/null
|
||||
|
||||
# If the web directory is empty after removing these files, then the parent html folder can be removed.
|
||||
if [ -d "/var/www/html" ]; then
|
||||
if [[ ! "$(ls -A /var/www/html)" ]]; then
|
||||
${SUDO} rm -rf /var/www/html &> /dev/null
|
||||
fi
|
||||
fi
|
||||
# If the web directory is empty after removing these files, then the parent html directory can be removed.
|
||||
if [ -d "/var/www/html" ]; then
|
||||
if [[ ! "$(ls -A /var/www/html)" ]]; then
|
||||
${SUDO} rm -rf /var/www/html &> /dev/null
|
||||
fi
|
||||
fi
|
||||
echo -e "${OVER} ${TICK} Removed Web Interface"
|
||||
|
||||
# Attempt to preserve backwards compatibility with older versions
|
||||
# to guarantee no additional changes were made to /etc/crontab after
|
||||
# the installation of pihole, /etc/crontab.pihole should be permanently
|
||||
# preserved.
|
||||
if [[ -f /etc/crontab.orig ]]; then
|
||||
${SUDO} mv /etc/crontab /etc/crontab.pihole
|
||||
${SUDO} mv /etc/crontab.orig /etc/crontab
|
||||
${SUDO} service cron restart
|
||||
echo -e " ${TICK} Restored the default system cron"
|
||||
fi
|
||||
|
||||
# Attempt to preserve backwards compatibility with older versions
|
||||
# to guarantee no additional changes were made to /etc/crontab after
|
||||
# the installation of pihole, /etc/crontab.pihole should be permanently
|
||||
# preserved.
|
||||
if [[ -f /etc/crontab.orig ]]; then
|
||||
echo "::: Initial Pi-hole cron detected. Restoring the default system cron..."
|
||||
${SUDO} mv /etc/crontab /etc/crontab.pihole
|
||||
${SUDO} mv /etc/crontab.orig /etc/crontab
|
||||
${SUDO} service cron restart
|
||||
fi
|
||||
# Attempt to preserve backwards compatibility with older versions
|
||||
if [[ -f /etc/cron.d/pihole ]];then
|
||||
${SUDO} rm -f /etc/cron.d/pihole &> /dev/null
|
||||
echo -e " ${TICK} Removed /etc/cron.d/pihole"
|
||||
fi
|
||||
|
||||
# Attempt to preserve backwards compatibility with older versions
|
||||
if [[ -f /etc/cron.d/pihole ]];then
|
||||
echo "::: Removing cron.d/pihole..."
|
||||
${SUDO} rm /etc/cron.d/pihole &> /dev/null
|
||||
fi
|
||||
package_check lighttpd > /dev/null
|
||||
if [[ $? -eq 1 ]]; then
|
||||
${SUDO} rm -rf /etc/lighttpd/ &> /dev/null
|
||||
echo -e " ${TICK} Removed lighttpd"
|
||||
else
|
||||
if [ -f /etc/lighttpd/lighttpd.conf.orig ]; then
|
||||
${SUDO} mv /etc/lighttpd/lighttpd.conf.orig /etc/lighttpd/lighttpd.conf
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "::: Removing config files and scripts..."
|
||||
package_check lighttpd > /dev/null
|
||||
if [ $? -eq 1 ]; then
|
||||
${SUDO} rm -rf /etc/lighttpd/ &> /dev/null
|
||||
else
|
||||
if [ -f /etc/lighttpd/lighttpd.conf.orig ]; then
|
||||
${SUDO} mv /etc/lighttpd/lighttpd.conf.orig /etc/lighttpd/lighttpd.conf
|
||||
fi
|
||||
fi
|
||||
${SUDO} rm -f /etc/dnsmasq.d/adList.conf &> /dev/null
|
||||
${SUDO} rm -f /etc/dnsmasq.d/01-pihole.conf &> /dev/null
|
||||
${SUDO} rm -rf /var/log/*pihole* &> /dev/null
|
||||
${SUDO} rm -rf /etc/pihole/ &> /dev/null
|
||||
${SUDO} rm -rf /etc/.pihole/ &> /dev/null
|
||||
${SUDO} rm -rf /opt/pihole/ &> /dev/null
|
||||
${SUDO} rm -f /usr/local/bin/pihole &> /dev/null
|
||||
${SUDO} rm -f /etc/bash_completion.d/pihole &> /dev/null
|
||||
${SUDO} rm -f /etc/sudoers.d/pihole &> /dev/null
|
||||
echo -e " ${TICK} Removed config files"
|
||||
|
||||
${SUDO} rm /etc/dnsmasq.d/adList.conf &> /dev/null
|
||||
${SUDO} rm /etc/dnsmasq.d/01-pihole.conf &> /dev/null
|
||||
${SUDO} rm -rf /var/log/*pihole* &> /dev/null
|
||||
${SUDO} rm -rf /etc/pihole/ &> /dev/null
|
||||
${SUDO} rm -rf /etc/.pihole/ &> /dev/null
|
||||
${SUDO} rm -rf /opt/pihole/ &> /dev/null
|
||||
${SUDO} rm /usr/local/bin/pihole &> /dev/null
|
||||
${SUDO} rm /etc/bash_completion.d/pihole &> /dev/null
|
||||
${SUDO} rm /etc/sudoers.d/pihole &> /dev/null
|
||||
# Restore Resolved
|
||||
if [[ -e /etc/systemd/resolved.conf.orig ]]; then
|
||||
${SUDO} cp /etc/systemd/resolved.conf.orig /etc/systemd/resolved.conf
|
||||
systemctl reload-or-restart systemd-resolved
|
||||
fi
|
||||
|
||||
# If the pihole user exists, then remove
|
||||
if id "pihole" >/dev/null 2>&1; then
|
||||
echo "::: Removing pihole user..."
|
||||
${SUDO} userdel -r pihole
|
||||
fi
|
||||
# Remove FTL
|
||||
if command -v pihole-FTL &> /dev/null; then
|
||||
echo -ne " ${INFO} Removing pihole-FTL..."
|
||||
if [[ -x "$(command -v systemctl)" ]]; then
|
||||
systemctl stop pihole-FTL
|
||||
else
|
||||
service pihole-FTL stop
|
||||
fi
|
||||
${SUDO} rm -f /etc/init.d/pihole-FTL
|
||||
${SUDO} rm -f /usr/bin/pihole-FTL
|
||||
echo -e "${OVER} ${TICK} Removed pihole-FTL"
|
||||
fi
|
||||
|
||||
echo ":::"
|
||||
printf "::: Finished removing PiHole from your system. Sorry to see you go!\n"
|
||||
printf "::: Reach out to us at https://github.com/pi-hole/pi-hole/issues if you need help\n"
|
||||
printf "::: Reinstall by simpling running\n:::\n:::\tcurl -sSL https://install.pi-hole.net | bash\n:::\n::: at any time!\n:::\n"
|
||||
printf "::: PLEASE RESET YOUR DNS ON YOUR ROUTER/CLIENTS TO RESTORE INTERNET CONNECTIVITY!\n"
|
||||
# If the pihole manpage exists, then delete and rebuild man-db
|
||||
if [[ -f /usr/local/share/man/man8/pihole.8 ]]; then
|
||||
${SUDO} rm -f /usr/local/share/man/man8/pihole.8 /usr/local/share/man/man8/pihole-FTL.8 /usr/local/share/man/man5/pihole-FTL.conf.5
|
||||
${SUDO} mandb -q &>/dev/null
|
||||
echo -e " ${TICK} Removed pihole man page"
|
||||
fi
|
||||
|
||||
# If the pihole user exists, then remove
|
||||
if id "pihole" &> /dev/null; then
|
||||
if ${SUDO} userdel -r pihole 2> /dev/null; then
|
||||
echo -e " ${TICK} Removed 'pihole' user"
|
||||
else
|
||||
echo -e " ${CROSS} Unable to remove 'pihole' user"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "\\n We're sorry to see you go, but thanks for checking out Pi-hole!
|
||||
If you need help, reach out to us on Github, Discourse, Reddit or Twitter
|
||||
Reinstall at any time: ${COL_WHITE}curl -sSL https://install.pi-hole.net | bash${COL_NC}
|
||||
|
||||
${COL_LIGHT_RED}Please reset the DNS on your router/clients to restore internet connectivity
|
||||
${COL_LIGHT_GREEN}Uninstallation Complete! ${COL_NC}"
|
||||
}
|
||||
|
||||
######### SCRIPT ###########
|
||||
echo "::: Preparing to remove packages, be sure that each may be safely removed depending on your operating system."
|
||||
echo "::: (SAFE TO REMOVE ALL ON RASPBIAN)"
|
||||
if command -v vcgencmd &> /dev/null; then
|
||||
echo -e " ${INFO} All dependencies are safe to remove on Raspbian"
|
||||
else
|
||||
echo -e " ${INFO} Be sure to confirm if any dependencies should not be removed"
|
||||
fi
|
||||
while true; do
|
||||
read -rp "::: Do you wish to purge PiHole's dependencies from your OS? (You will be prompted for each package) [y/n]: " yn
|
||||
case ${yn} in
|
||||
[Yy]* ) removeAndPurge; break;;
|
||||
|
||||
[Nn]* ) removeNoPurge; break;;
|
||||
esac
|
||||
echo -e " ${INFO} ${COL_YELLOW}The following dependencies may have been added by the Pi-hole install:"
|
||||
echo -n " "
|
||||
for i in "${DEPS[@]}"; do
|
||||
echo -n "${i} "
|
||||
done
|
||||
echo "${COL_NC}"
|
||||
read -rp " ${QST} Do you wish to go through each dependency for removal? (Choosing No will leave all dependencies installed) [Y/n] " yn
|
||||
case ${yn} in
|
||||
[Yy]* ) removeAndPurge; break;;
|
||||
[Nn]* ) removeNoPurge; break;;
|
||||
* ) removeAndPurge; break;;
|
||||
esac
|
||||
done
|
||||
|
1130
gravity.sh
1130
gravity.sh
File diff suppressed because it is too large
Load Diff
112
manpages/pihole-FTL.8
Normal file
112
manpages/pihole-FTL.8
Normal file
@@ -0,0 +1,112 @@
|
||||
.TH "Pihole-FTL" "8" "pihole-FTL" "Pi-hole" "June 2018"
|
||||
.SH "NAME"
|
||||
pihole-FTL - Pi-hole : The Faster-Than-Light (FTL) Engine
|
||||
.br
|
||||
.SH "SYNOPSIS"
|
||||
\fBservice pihole-FTL \fR(\fBstart\fR|\fBstop\fR|\fBrestart\fR)
|
||||
.br
|
||||
|
||||
\fBpihole-FTL debug\fR
|
||||
.br
|
||||
\fBpihole-FTL test\fR
|
||||
.br
|
||||
\fBpihole-FTL -v\fR
|
||||
.br
|
||||
\fBpihole-FTL -t\fR
|
||||
.br
|
||||
\fBpihole-FTL -b\fR
|
||||
.br
|
||||
\fBpihole-FTL -f\fR
|
||||
.br
|
||||
\fBpihole-FTL -h\fR
|
||||
.br
|
||||
\fBpihole-FTL dnsmasq-test\fR
|
||||
.br
|
||||
\fBpihole-FTL --\fR (\fBoptions\fR)
|
||||
.br
|
||||
|
||||
.SH "DESCRIPTION"
|
||||
Pi-hole : The Faster-Than-Light (FTL) Engine is a lightweight, purpose-built daemon used to provide statistics needed for the Pi-hole Web Interface, and its API can be easily integrated into your own projects. Although it is an optional component of the Pi-hole ecosystem, it will be installed by default to provide statistics. As the name implies, FTL does its work \fIvery\fR \fIquickly\fR!
|
||||
.br
|
||||
|
||||
Usage
|
||||
.br
|
||||
|
||||
\fBservice pihole-FTL start\fR
|
||||
.br
|
||||
Start the pihole-FTL daemon
|
||||
.br
|
||||
|
||||
\fBservice pihole-FTL stop\fR
|
||||
.br
|
||||
Stop the pihole-FTL daemon
|
||||
.br
|
||||
|
||||
\fBservice pihole-FTL restart\fR
|
||||
.br
|
||||
If the pihole-FTP daemon is running, stop and then start, otherwise start.
|
||||
.br
|
||||
|
||||
Command line arguments
|
||||
.br
|
||||
|
||||
\fBdebug\fR
|
||||
.br
|
||||
Don't go into daemon mode (stay in foreground) + more verbose logging
|
||||
.br
|
||||
|
||||
\fBtest\fR
|
||||
.br
|
||||
Start FTL and process everything, but shut down immediately afterwards
|
||||
.br
|
||||
|
||||
\fB-v, version\fR
|
||||
.br
|
||||
Don't start FTL, show only version
|
||||
.br
|
||||
|
||||
\fB-t, tag\fR
|
||||
.br
|
||||
Don't start FTL, show only git tag
|
||||
.br
|
||||
|
||||
\fB-b, branch\fR
|
||||
.br
|
||||
Don't start FTL, show only git branch FTL was compiled from
|
||||
.br
|
||||
|
||||
\fB-f, no-daemon\fR
|
||||
.br
|
||||
Don't go into background (daemon mode)
|
||||
.br
|
||||
|
||||
\fB-h, help\fR
|
||||
.br
|
||||
Don't start FTL, show help
|
||||
.br
|
||||
|
||||
\fBdnsmasq-test\fR
|
||||
.br
|
||||
Test resolver config file syntax
|
||||
.br
|
||||
|
||||
\fB--\fR (options)
|
||||
.br
|
||||
Pass options to internal dnsmasq resolver
|
||||
.br
|
||||
.SH "EXAMPLE"
|
||||
Command line arguments can be arbitrarily combined, e.g:
|
||||
.br
|
||||
|
||||
\fBpihole-FTL debug test\fR
|
||||
.br
|
||||
|
||||
Start ftl in foreground with more verbose logging, process everything and shutdown immediately
|
||||
.br
|
||||
.SH "SEE ALSO"
|
||||
\fBpihole\fR(8), \fBpihole-FTL.conf\fR(5)
|
||||
.br
|
||||
.SH "COLOPHON"
|
||||
|
||||
Get sucked into the latest news and community activity by entering Pi-hole's orbit. Information about Pi-hole, and the latest version of the software can be found at https://pi-hole.net
|
||||
.br
|
102
manpages/pihole-FTL.conf.5
Normal file
102
manpages/pihole-FTL.conf.5
Normal file
@@ -0,0 +1,102 @@
|
||||
.TH "pihole-FTL.conf" "5" "pihole-FTL.conf" "pihole-FTL.conf" "June 2018"
|
||||
.SH "NAME"
|
||||
|
||||
pihole-FTL.conf - FTL's config file
|
||||
.br
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
/etc/pihole/pihole-FTL.conf will be read by \fBpihole-FTL(8)\fR on startup.
|
||||
.br
|
||||
|
||||
\fBSOCKET_LISTENING=localonly|all\fR
|
||||
.br
|
||||
Listen only for local socket connections or permit all connections
|
||||
.br
|
||||
|
||||
\fBQUERY_DISPLAY=yes|no\fR
|
||||
.br
|
||||
Display all queries? Set to no to hide query display
|
||||
.br
|
||||
|
||||
\fBAAAA_QUERY_ANALYSIS=yes|no\fR
|
||||
.br
|
||||
Allow FTL to analyze AAAA queries from pihole.log?
|
||||
.br
|
||||
|
||||
\fBRESOLVE_IPV6=yes|no\fR
|
||||
.br
|
||||
Should FTL try to resolve IPv6 addresses to host names?
|
||||
.br
|
||||
|
||||
\fBRESOLVE_IPV4=yes|no\fR
|
||||
.br
|
||||
Should FTL try to resolve IPv4 addresses to host names?
|
||||
.br
|
||||
|
||||
\fBMAXDBDAYS=365\fR
|
||||
.br
|
||||
How long should queries be stored in the database?
|
||||
.br
|
||||
Setting this to 0 disables the database
|
||||
.br
|
||||
|
||||
\fBDBINTERVAL=1.0\fR
|
||||
.br
|
||||
How often do we store queries in FTL's database [minutes]?
|
||||
.br
|
||||
|
||||
\fBDBFILE=/etc/pihole/pihole-FTL.db\fR
|
||||
.br
|
||||
Specify path and filename of FTL's SQLite long-term database.
|
||||
.br
|
||||
Setting this to DBFILE= disables the database altogether
|
||||
.br
|
||||
|
||||
\fBMAXLOGAGE=24.0\fR
|
||||
.br
|
||||
Up to how many hours of queries should be imported from the database and logs?
|
||||
.br
|
||||
Maximum is 744 (31 days)
|
||||
.br
|
||||
|
||||
\fBFTLPORT=4711\fR
|
||||
.br
|
||||
On which port should FTL be listening?
|
||||
.br
|
||||
|
||||
\fBPRIVACYLEVEL=0|1|2|3\fR
|
||||
.br
|
||||
Which privacy level is used?
|
||||
.br
|
||||
0 - show everything
|
||||
.br
|
||||
1 - hide domains
|
||||
.br
|
||||
2 - hide domains and clients
|
||||
.br
|
||||
3 - paranoia mode (hide everything)
|
||||
.br
|
||||
|
||||
\fBIGNORE_LOCALHOST=no|yes\fR
|
||||
.br
|
||||
Should FTL ignore queries coming from the local machine?
|
||||
.br
|
||||
|
||||
\fBBLOCKINGMODE=IP|IP-AAAA-NODATA|NXDOMAIN|NULL\fR
|
||||
.br
|
||||
How should FTL reply to blocked queries?
|
||||
.br
|
||||
|
||||
For each setting, the option shown first is the default.
|
||||
.br
|
||||
.SH "SEE ALSO"
|
||||
|
||||
\fBpihole\fR(8), \fBpihole-FTL\fR(8)
|
||||
.br
|
||||
.SH "COLOPHON"
|
||||
|
||||
Pi-hole : The Faster-Than-Light (FTL) Engine is a lightweight, purpose-built daemon used to provide statistics needed for the Pi-hole Web Interface, and its API can be easily integrated into your own projects. Although it is an optional component of the Pi-hole ecosystem, it will be installed by default to provide statistics. As the name implies, FTL does its work \fIvery quickly\fR!
|
||||
.br
|
||||
|
||||
Get sucked into the latest news and community activity by entering Pi-hole's orbit. Information about Pi-hole, and the latest version of the software can be found at https://pi-hole.net
|
||||
.br
|
361
manpages/pihole.8
Normal file
361
manpages/pihole.8
Normal file
@@ -0,0 +1,361 @@
|
||||
.TH "Pi-hole" "8" "Pi-hole" "Pi-hole" "May 2018"
|
||||
.SH "NAME"
|
||||
|
||||
Pi-hole : A black-hole for internet advertisements
|
||||
.br
|
||||
.SH "SYNOPSIS"
|
||||
|
||||
\fBpihole\fR (\fB-w\fR|\fB-b\fR|\fB--wild\fR|\fB--regex\fR) [options] domain(s)
|
||||
.br
|
||||
\fBpihole -a\fR \fB-p\fR password
|
||||
.br
|
||||
\fBpihole -a\fR (\fB-c|-f|-k\fR)
|
||||
.br
|
||||
\fBpihole -a\fR [\fB-r\fR hostrecord]
|
||||
.br
|
||||
\fBpihole -a -e\fR email
|
||||
.br
|
||||
\fBpihole -a -i\fR interface
|
||||
.br
|
||||
\fBpihole -a -l\fR privacylevel
|
||||
.br
|
||||
\fBpihole -c\fR [-j|-r|-e]
|
||||
.br
|
||||
\fBpihole\fR \fB-d\fR [-a]
|
||||
.br
|
||||
\fBpihole -f
|
||||
.br
|
||||
pihole -r
|
||||
.br
|
||||
pihole -t
|
||||
.br
|
||||
pihole -g\fR
|
||||
.br
|
||||
\fBpihole\fR -\fBq\fR [options]
|
||||
.br
|
||||
\fBpihole\fR \fB-l\fR (\fBon|off|off noflush\fR)
|
||||
.br
|
||||
\fBpihole -up \fR[--checkonly]
|
||||
.br
|
||||
\fBpihole -v\fR [-p|-a|-f] [-c|-l|-hash]
|
||||
.br
|
||||
\fBpihole uninstall
|
||||
.br
|
||||
pihole status
|
||||
.br
|
||||
pihole restartdns\fR
|
||||
.br
|
||||
\fBpihole\fR (\fBenable\fR|\fBdisable\fR [time])
|
||||
.br
|
||||
\fBpihole\fR \fBcheckout\fR repo [branch]
|
||||
.br
|
||||
\fBpihole\fR \fBhelp\fR
|
||||
.br
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
Available commands and options:
|
||||
.br
|
||||
|
||||
\fB-w, whitelist\fR [options] [<domain1> <domain2 ...>]
|
||||
.br
|
||||
Adds or removes specified domain or domains tho the Whitelist
|
||||
.br
|
||||
|
||||
\fB-b, blacklist\fR [options] [<domain1> <domain2 ...>]
|
||||
.br
|
||||
Adds or removes specified domain or domains to the blacklist
|
||||
.br
|
||||
|
||||
\fB--wild, wildcard\fR [options] [<domain1> <domain2 ...>]
|
||||
.br
|
||||
Add or removes specified domain to the wildcard blacklist
|
||||
.br
|
||||
|
||||
\fB--regex, regex\fR [options] [<regex1> <regex2 ...>]
|
||||
.br
|
||||
Add or removes specified regex filter to the regex blacklist
|
||||
.br
|
||||
|
||||
(Whitelist/Blacklist manipulation options):
|
||||
.br
|
||||
-d, --delmode Remove domain(s) from the list
|
||||
.br
|
||||
-nr, --noreload Update list without refreshing dnsmasq
|
||||
.br
|
||||
-q, --quiet Make output less verbose
|
||||
.br
|
||||
-l, --list Display all your listed domains
|
||||
.br
|
||||
--nuke Removes all entries in a list
|
||||
.br
|
||||
|
||||
\fB-d, debug\fR [-a]
|
||||
.br
|
||||
Start a debugging session
|
||||
.br
|
||||
|
||||
-a Enable automated debugging
|
||||
.br
|
||||
|
||||
\fB-f, flush\fR
|
||||
.br
|
||||
Flush the Pi-hole log
|
||||
.br
|
||||
|
||||
\fB-r, reconfigure\fR
|
||||
.br
|
||||
Reconfigure or Repair Pi-hole subsystems
|
||||
.br
|
||||
|
||||
\fB-t, tail\fR
|
||||
.br
|
||||
View the live output of the Pi-hole log
|
||||
.br
|
||||
|
||||
\fB-a, admin\fR [options]
|
||||
.br
|
||||
|
||||
(Admin options):
|
||||
.br
|
||||
-p, password Set Web Interface password
|
||||
.br
|
||||
-c, celsius Set Celsius as preferred temperature unit
|
||||
.br
|
||||
-f, fahrenheit Set Fahrenheit as preferred temperature unit
|
||||
.br
|
||||
-k, kelvin Set Kelvin as preferred temperature unit
|
||||
.br
|
||||
-r, hostrecord Add a name to the DNS associated to an
|
||||
IPv4/IPv6 address
|
||||
.br
|
||||
-e, email Set an administrative contact address for the
|
||||
Block Page
|
||||
.br
|
||||
-i, interface Specify dnsmasq's interface listening behavior
|
||||
.br
|
||||
-l, privacylevel <level> Set privacy level
|
||||
(0 = lowest, 3 = highest)
|
||||
.br
|
||||
|
||||
\fB-c, chronometer\fR [options]
|
||||
.br
|
||||
Calculates stats and displays to an LCD
|
||||
.br
|
||||
|
||||
(Chronometer Options):
|
||||
.br
|
||||
-j, --json Output stats as JSON formatted string
|
||||
.br
|
||||
-r, --refresh Set update frequency (in seconds)
|
||||
.br
|
||||
-e, --exit Output stats and exit witout refreshing
|
||||
.br
|
||||
|
||||
\fB-g, updateGravity\fR
|
||||
.br
|
||||
Update the list of ad-serving domains
|
||||
.br
|
||||
|
||||
\fB-q, query\fR [option]
|
||||
.br
|
||||
Query the adlists for a specified domain
|
||||
.br
|
||||
|
||||
(Query options):
|
||||
.br
|
||||
-adlist Print the name of the block list URL
|
||||
.br
|
||||
-exact Search the block lists for exact domain matches
|
||||
.br
|
||||
-all Return all query matches within a block list
|
||||
.br
|
||||
|
||||
\fB-h, --help, help\fR
|
||||
.br
|
||||
Show a help dialog
|
||||
.br
|
||||
|
||||
\fB-l, logging\fR [on|off|off noflush]
|
||||
.br
|
||||
Specify whether the Pi-hole log should be used
|
||||
.br
|
||||
|
||||
(Logging options):
|
||||
.br
|
||||
on Enable the Pi-hole log at /var/log/pihole.log
|
||||
.br
|
||||
off Disable and flush the Pi-hole log at
|
||||
/var/log/pihole.log
|
||||
.br
|
||||
off noflush Disable the Pi-hole log at /var/log/pihole.log
|
||||
.br
|
||||
|
||||
\fB-up, updatePihole\fR [--check-only]
|
||||
.br
|
||||
Update Pi-hole subsystems
|
||||
.br
|
||||
|
||||
--check-only Exit script before update is performed.
|
||||
.br
|
||||
|
||||
\fB-v, version\fR [repo] [options]
|
||||
.br
|
||||
Show installed versions of Pi-hole, Web Interface & FTL
|
||||
.br
|
||||
|
||||
.br
|
||||
(repo options):
|
||||
.br
|
||||
-p, --pihole Only retrieve info regarding Pi-hole repository
|
||||
.br
|
||||
-a, --admin Only retrieve info regarding AdminLTE
|
||||
repository
|
||||
.br
|
||||
-f, --ftl Only retrieve info regarding FTL repository
|
||||
.br
|
||||
(version options):
|
||||
.br
|
||||
-c, --current Return the current version
|
||||
.br
|
||||
-l, --latest Return the latest version
|
||||
.br
|
||||
--hash Return the Github hash from your local
|
||||
repositories
|
||||
.br
|
||||
|
||||
\fBuninstall\fR
|
||||
.br
|
||||
Uninstall Pi-hole from your system
|
||||
.br
|
||||
|
||||
\fBstatus\fR
|
||||
.br
|
||||
Display the running status of Pi-hole subsystems
|
||||
.br
|
||||
|
||||
\fBenable\fR
|
||||
.br
|
||||
Enable Pi-hole subsystems
|
||||
.br
|
||||
|
||||
\fBdisable\fR [time]
|
||||
.br
|
||||
Disable Pi-hole subsystems, optionally for a set duration
|
||||
.br
|
||||
|
||||
(time options):
|
||||
.br
|
||||
#s Disable Pi-hole functionality for # second(s)
|
||||
.br
|
||||
#m Disable Pi-hole functionality for # minute(s)
|
||||
.br
|
||||
|
||||
\fBrestartdns\fR
|
||||
.br
|
||||
Restart Pi-hole subsystems
|
||||
.br
|
||||
|
||||
\fBcheckout\fR [repo] [branch]
|
||||
.br
|
||||
Switch Pi-hole subsystems to a different Github branch
|
||||
.br
|
||||
|
||||
(repo options):
|
||||
.br
|
||||
core Change the branch of Pi-hole's core subsystem
|
||||
.br
|
||||
web Change the branch of Admin Console subsystem
|
||||
.br
|
||||
ftl Change the branch of Pi-hole's FTL subsystem
|
||||
.br
|
||||
(branch options):
|
||||
.br
|
||||
master Update subsystems to the latest stable release
|
||||
.br
|
||||
dev Update subsystems to the latest development
|
||||
release
|
||||
.br
|
||||
branchname Update subsystems to the specified branchname
|
||||
.br
|
||||
.SH "EXAMPLE"
|
||||
|
||||
Some usage examples
|
||||
.br
|
||||
|
||||
Whitelist/blacklist manipulation
|
||||
.br
|
||||
|
||||
\fBpihole -w iloveads.example.com\fR
|
||||
.br
|
||||
Adds "iloveads.example.com" to whitelist
|
||||
.br
|
||||
|
||||
\fBpihole -b -d noads.example.com\fR
|
||||
.br
|
||||
Removes "noads.example.com" from blacklist
|
||||
.br
|
||||
|
||||
\fBpihole --wild example.com\fR
|
||||
.br
|
||||
Adds example.com as a wildcard - would block all subdomains of
|
||||
example.com, including example.com itself.
|
||||
.br
|
||||
|
||||
\fBpihole --regex "ad.*\\.example\\.com$"\fR
|
||||
.br
|
||||
Adds "ad.*\\.example\\.com$" to the regex blacklist.
|
||||
Would block all subdomains of example.com which start with "ad"
|
||||
.br
|
||||
|
||||
Changing the Web Interface password
|
||||
.br
|
||||
|
||||
\fBpihole -a -p ExamplePassword\fR
|
||||
.br
|
||||
Change the password to "ExamplePassword"
|
||||
.br
|
||||
|
||||
Updating lists from internet sources
|
||||
.br
|
||||
|
||||
\fBpihole -g\fR
|
||||
.br
|
||||
Update the list of ad-serving domains
|
||||
.br
|
||||
|
||||
Displaying version information
|
||||
.br
|
||||
|
||||
\fBpihole -v -a -c\fR
|
||||
.br
|
||||
Display the current version of AdminLTE
|
||||
.br
|
||||
|
||||
Temporarily disabling Pi-hole
|
||||
.br
|
||||
|
||||
\fBpihole disable 5m\fR
|
||||
.br
|
||||
Disable Pi-hole functionality for five minutes
|
||||
.br
|
||||
|
||||
Switching Pi-hole subsystem branches
|
||||
.br
|
||||
|
||||
\fBpihole checkout master\fR
|
||||
.br
|
||||
Switch to master branch
|
||||
.br
|
||||
|
||||
\fBpihole checkout core dev\fR
|
||||
.br
|
||||
Switch to core development branch
|
||||
.br
|
||||
.SH "SEE ALSO"
|
||||
|
||||
\fBlighttpd\fR(8), \fBpihole-FTL\fR(8)
|
||||
.br
|
||||
.SH "COLOPHON"
|
||||
|
||||
Get sucked into the latest news and community activity by entering Pi-hole's orbit. Information about Pi-hole, and the latest version of the software can be found at https://pi-hole.net.
|
||||
.br
|
365
pihole
365
pihole
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
@@ -9,7 +10,18 @@
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||
readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
|
||||
readonly gravitylist="/etc/pihole/gravity.list"
|
||||
readonly blacklist="/etc/pihole/black.list"
|
||||
|
||||
# setupVars is not readonly here because in some funcitons (checkout),
|
||||
# it might get set again when the installer is sourced. This causes an
|
||||
# error due to modifying a readonly variable.
|
||||
setupVars="/etc/pihole/setupVars.conf"
|
||||
|
||||
readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE"
|
||||
source "${colfile}"
|
||||
|
||||
resolver="pihole-FTL"
|
||||
|
||||
# Must be root to use this tool
|
||||
if [[ ! $EUID -eq 0 ]];then
|
||||
@@ -17,28 +29,18 @@ if [[ ! $EUID -eq 0 ]];then
|
||||
exec sudo bash "$0" "$@"
|
||||
exit $?
|
||||
else
|
||||
echo "::: sudo is needed to run pihole commands. Please run this script as root or install sudo."
|
||||
echo -e " ${CROSS} sudo is needed to run pihole commands. Please run this script as root or install sudo."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
webpageFunc() {
|
||||
source /opt/pihole/webpage.sh
|
||||
source "${PI_HOLE_SCRIPT_DIR}/webpage.sh"
|
||||
main "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
whitelistFunc() {
|
||||
"${PI_HOLE_SCRIPT_DIR}"/list.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
blacklistFunc() {
|
||||
"${PI_HOLE_SCRIPT_DIR}"/list.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
wildcardFunc() {
|
||||
listFunc() {
|
||||
"${PI_HOLE_SCRIPT_DIR}"/list.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
@@ -66,7 +68,8 @@ flushFunc() {
|
||||
}
|
||||
|
||||
updatePiholeFunc() {
|
||||
"${PI_HOLE_SCRIPT_DIR}"/update.sh
|
||||
shift
|
||||
"${PI_HOLE_SCRIPT_DIR}"/update.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -77,69 +80,12 @@ reconfigurePiholeFunc() {
|
||||
|
||||
updateGravityFunc() {
|
||||
"${PI_HOLE_SCRIPT_DIR}"/gravity.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
scanList(){
|
||||
domain="${1}"
|
||||
list="${2}"
|
||||
method="${3}"
|
||||
if [[ ${method} == "-exact" ]] ; then
|
||||
grep -i -E "(^|\s)${domain}($|\s)" "${list}"
|
||||
else
|
||||
grep -i "${domain}" "${list}"
|
||||
fi
|
||||
}
|
||||
|
||||
processWildcards() {
|
||||
IFS="." read -r -a array <<< "${1}"
|
||||
for (( i=${#array[@]}-1; i>=0; i-- )); do
|
||||
ar=""
|
||||
for (( j=${#array[@]}-1; j>${#array[@]}-i-2; j-- )); do
|
||||
if [[ $j == $((${#array[@]}-1)) ]]; then
|
||||
ar="${array[$j]}"
|
||||
else
|
||||
ar="${array[$j]}.${ar}"
|
||||
fi
|
||||
done
|
||||
echo "${ar}"
|
||||
done
|
||||
exit $?
|
||||
}
|
||||
|
||||
queryFunc() {
|
||||
domain="${2}"
|
||||
method="${3}"
|
||||
lists=( /etc/pihole/list.* /etc/pihole/blacklist.txt)
|
||||
for list in ${lists[@]}; do
|
||||
if [ -e "${list}" ]; then
|
||||
result=$(scanList ${domain} ${list} ${method})
|
||||
# Remove empty lines before couting number of results
|
||||
count=$(sed '/^\s*$/d' <<< "$result" | wc -l)
|
||||
echo "::: ${list} (${count} results)"
|
||||
if [[ ${count} > 0 ]]; then
|
||||
echo "${result}"
|
||||
fi
|
||||
echo ""
|
||||
else
|
||||
echo "::: ${list} does not exist"
|
||||
echo ""
|
||||
fi
|
||||
done
|
||||
|
||||
# Scan for possible wildcard matches
|
||||
if [ -e "${wildcardlist}" ]; then
|
||||
local wildcards=($(processWildcards "${domain}"))
|
||||
for domain in ${wildcards[@]}; do
|
||||
result=$(scanList "\/${domain}\/" ${wildcardlist})
|
||||
# Remove empty lines before couting number of results
|
||||
count=$(sed '/^\s*$/d' <<< "$result" | wc -l)
|
||||
if [[ ${count} > 0 ]]; then
|
||||
echo "::: Wildcard blocking ${domain} (${count} results)"
|
||||
echo "${result}"
|
||||
echo ""
|
||||
fi
|
||||
done
|
||||
fi
|
||||
shift
|
||||
"${PI_HOLE_SCRIPT_DIR}"/query.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -162,21 +108,37 @@ versionFunc() {
|
||||
}
|
||||
|
||||
restartDNS() {
|
||||
dnsmasqPid=$(pidof dnsmasq)
|
||||
if [[ "${dnsmasqPid}" ]]; then
|
||||
# Service already running - reload config
|
||||
if [[ -x "$(command -v systemctl)" ]]; then
|
||||
systemctl restart dnsmasq
|
||||
else
|
||||
service dnsmasq restart
|
||||
fi
|
||||
local svcOption svc str output status
|
||||
svcOption="${1:-}"
|
||||
|
||||
# Determine if we should reload or restart restart
|
||||
if [[ "${svcOption}" =~ "reload" ]]; then
|
||||
# Using SIGHUP will NOT re-read any *.conf files
|
||||
svc="killall -s SIGHUP ${resolver}"
|
||||
else
|
||||
# Service not running, start it up
|
||||
if [[ -x "$(command -v systemctl)" ]]; then
|
||||
systemctl start dnsmasq
|
||||
# Get PID of resolver to determine if it needs to start or restart
|
||||
if pidof pihole-FTL &> /dev/null; then
|
||||
svcOption="restart"
|
||||
else
|
||||
service dnsmasq start
|
||||
svcOption="start"
|
||||
fi
|
||||
svc="service ${resolver} ${svcOption}"
|
||||
fi
|
||||
|
||||
# Print output to Terminal, but not to Web Admin
|
||||
str="${svcOption^}ing DNS service"
|
||||
[[ -t 1 ]] && echo -ne " ${INFO} ${str}..."
|
||||
|
||||
output=$( { ${svc}; } 2>&1 )
|
||||
status="$?"
|
||||
|
||||
if [[ "${status}" -eq 0 ]]; then
|
||||
[[ -t 1 ]] && echo -e "${OVER} ${TICK} ${str}"
|
||||
return 0
|
||||
else
|
||||
[[ ! -t 1 ]] && local OVER=""
|
||||
echo -e "${OVER} ${CROSS} ${output}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -190,41 +152,74 @@ Time:
|
||||
#s Disable Pi-hole functionality for # second(s)
|
||||
#m Disable Pi-hole functionality for # minute(s)"
|
||||
exit 0
|
||||
|
||||
elif [[ "${1}" == "0" ]]; then
|
||||
# Disable Pi-hole
|
||||
sed -i 's/^addn-hosts=\/etc\/pihole\/gravity.list/#addn-hosts=\/etc\/pihole\/gravity.list/' /etc/dnsmasq.d/01-pihole.conf
|
||||
sed -i 's/^addn-hosts=\/etc\/pihole\/black.list/#addn-hosts=\/etc\/pihole\/black.list/' /etc/dnsmasq.d/01-pihole.conf
|
||||
if [[ -e "$wildcardlist" ]]; then
|
||||
mv "$wildcardlist" "/etc/pihole/wildcard.list"
|
||||
if [[ -e "${gravitylist}" ]]; then
|
||||
mv "${gravitylist}" "${gravitylist}.bck"
|
||||
echo "" > "${gravitylist}"
|
||||
fi
|
||||
if [[ -e "${blacklist}" ]]; then
|
||||
mv "${blacklist}" "${blacklist}.bck"
|
||||
echo "" > "${blacklist}"
|
||||
fi
|
||||
echo "::: Blocking has been disabled!"
|
||||
if [[ $# > 1 ]]; then
|
||||
if [[ "${2}" == *"s"* ]]; then
|
||||
local error=false
|
||||
if [[ "${2}" == *"s" ]]; then
|
||||
tt=${2%"s"}
|
||||
echo "::: Blocking will be re-enabled in ${tt} seconds"
|
||||
nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null &
|
||||
elif [[ "${2}" == *"m"* ]]; then
|
||||
if [[ "${tt}" =~ ^-?[0-9]+$ ]];then
|
||||
local str="Disabling blocking for ${tt} seconds"
|
||||
echo -e " ${INFO} ${str}..."
|
||||
local str="Blocking will be re-enabled in ${tt} seconds"
|
||||
nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null &
|
||||
else
|
||||
local error=true
|
||||
fi
|
||||
elif [[ "${2}" == *"m" ]]; then
|
||||
tt=${2%"m"}
|
||||
echo "::: Blocking will be re-enabled in ${tt} minutes"
|
||||
tt=$((${tt}*60))
|
||||
nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null &
|
||||
if [[ "${tt}" =~ ^-?[0-9]+$ ]];then
|
||||
local str="Disabling blocking for ${tt} minutes"
|
||||
echo -e " ${INFO} ${str}..."
|
||||
local str="Blocking will be re-enabled in ${tt} minutes"
|
||||
tt=$((${tt}*60))
|
||||
nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null &
|
||||
else
|
||||
local error=true
|
||||
fi
|
||||
elif [[ -n "${2}" ]]; then
|
||||
local error=true
|
||||
else
|
||||
echo "::: Unknown format for delayed reactivation of the blocking!"
|
||||
echo "::: Example:"
|
||||
echo "::: pihole disable 5s - will disable blocking for 5 seconds"
|
||||
echo "::: pihole disable 7m - will disable blocking for 7 minutes"
|
||||
echo "::: Blocking will not automatically be re-enabled!"
|
||||
echo -e " ${INFO} Disabling blocking"
|
||||
fi
|
||||
|
||||
if [[ ${error} == true ]];then
|
||||
echo -e " ${COL_LIGHT_RED}Unknown format for delayed reactivation of the blocking!${COL_NC}"
|
||||
echo -e " Try 'pihole disable --help' for more information."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local str="Pi-hole Disabled"
|
||||
sed -i "/BLOCKING_ENABLED=/d" "${setupVars}"
|
||||
echo "BLOCKING_ENABLED=false" >> "${setupVars}"
|
||||
fi
|
||||
else
|
||||
# Enable Pi-hole
|
||||
echo "::: Blocking has been enabled!"
|
||||
sed -i 's/^#addn-hosts/addn-hosts/' /etc/dnsmasq.d/01-pihole.conf
|
||||
if [[ -e "/etc/pihole/wildcard.list" ]]; then
|
||||
mv "/etc/pihole/wildcard.list" "$wildcardlist"
|
||||
echo -e " ${INFO} Enabling blocking"
|
||||
local str="Pi-hole Enabled"
|
||||
|
||||
if [[ -e "${gravitylist}.bck" ]]; then
|
||||
mv "${gravitylist}.bck" "${gravitylist}"
|
||||
fi
|
||||
if [[ -e "${blacklist}.bck" ]]; then
|
||||
mv "${blacklist}.bck" "${blacklist}"
|
||||
fi
|
||||
sed -i "/BLOCKING_ENABLED=/d" "${setupVars}"
|
||||
echo "BLOCKING_ENABLED=true" >> "${setupVars}"
|
||||
fi
|
||||
restartDNS
|
||||
|
||||
restartDNS reload
|
||||
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
}
|
||||
|
||||
piholeLogging() {
|
||||
@@ -236,70 +231,94 @@ Specify whether the Pi-hole log should be used
|
||||
|
||||
Options:
|
||||
on Enable the Pi-hole log at /var/log/pihole.log
|
||||
off Disable the Pi-hole log at /var/log/pihole.log"
|
||||
off Disable and flush the Pi-hole log at /var/log/pihole.log
|
||||
off noflush Disable the Pi-hole log at /var/log/pihole.log"
|
||||
exit 0
|
||||
elif [[ "${1}" == "off" ]]; then
|
||||
# Disable logging
|
||||
sed -i 's/^log-queries/#log-queries/' /etc/dnsmasq.d/01-pihole.conf
|
||||
sed -i 's/^QUERY_LOGGING=true/QUERY_LOGGING=false/' /etc/pihole/setupVars.conf
|
||||
pihole -f
|
||||
echo "::: Logging has been disabled!"
|
||||
if [[ "${2}" != "noflush" ]]; then
|
||||
# Flush logs
|
||||
pihole -f
|
||||
fi
|
||||
echo -e " ${INFO} Disabling logging..."
|
||||
local str="Logging has been disabled!"
|
||||
elif [[ "${1}" == "on" ]]; then
|
||||
# Enable logging
|
||||
sed -i 's/^#log-queries/log-queries/' /etc/dnsmasq.d/01-pihole.conf
|
||||
sed -i 's/^QUERY_LOGGING=false/QUERY_LOGGING=true/' /etc/pihole/setupVars.conf
|
||||
echo "::: Logging has been enabled!"
|
||||
echo -e " ${INFO} Enabling logging..."
|
||||
local str="Logging has been enabled!"
|
||||
else
|
||||
echo "::: Invalid option passed, please pass 'on' or 'off'"
|
||||
echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC}
|
||||
Try 'pihole logging --help' for more information."
|
||||
exit 1
|
||||
fi
|
||||
restartDNS
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
}
|
||||
|
||||
piholeStatus() {
|
||||
if [[ "$(netstat -plnt | grep -c ':53 ')" -gt "0" ]]; then
|
||||
statusFunc() {
|
||||
# Determine if service is running on port 53 (Cr: https://superuser.com/a/806331)
|
||||
if (echo > /dev/tcp/127.0.0.1/53) >/dev/null 2>&1; then
|
||||
if [[ "${1}" != "web" ]]; then
|
||||
echo "::: DNS service is running"
|
||||
echo -e " ${TICK} DNS service is running"
|
||||
fi
|
||||
else
|
||||
if [[ "${1}" == "web" ]]; then
|
||||
echo "-1";
|
||||
else
|
||||
echo "::: DNS service is NOT running"
|
||||
fi
|
||||
return
|
||||
case "${1}" in
|
||||
"web") echo "-1";;
|
||||
*) echo -e " ${CROSS} DNS service is NOT running";;
|
||||
esac
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$(grep -i "^#addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)" ]]; then
|
||||
# List is commented out
|
||||
if [[ "${1}" == "web" ]]; then
|
||||
echo 0;
|
||||
else
|
||||
echo "::: Pi-hole blocking is Disabled";
|
||||
fi
|
||||
elif [[ "$(grep -i "^addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)" ]]; then
|
||||
# List set
|
||||
if [[ "${1}" == "web" ]]; then
|
||||
echo 1;
|
||||
else
|
||||
echo "::: Pi-hole blocking is Enabled";
|
||||
fi
|
||||
# Determine if Pi-hole's blocking is enabled
|
||||
if grep -q "BLOCKING_ENABLED=false" /etc/pihole/setupVars.conf; then
|
||||
# A config is commented out
|
||||
case "${1}" in
|
||||
"web") echo 0;;
|
||||
*) echo -e " ${CROSS} Pi-hole blocking is Disabled";;
|
||||
esac
|
||||
elif grep -q "BLOCKING_ENABLED=true" /etc/pihole/setupVars.conf; then
|
||||
# Configs are set
|
||||
case "${1}" in
|
||||
"web") echo 1;;
|
||||
*) echo -e " ${TICK} Pi-hole blocking is Enabled";;
|
||||
esac
|
||||
else
|
||||
# Addn-host not found
|
||||
if [[ "${1}" == "web" ]]; then
|
||||
echo 99
|
||||
else
|
||||
echo "::: No hosts file linked to dnsmasq, adding it in enabled state"
|
||||
fi
|
||||
# Add addn-host= to dnsmasq
|
||||
echo "addn-hosts=/etc/pihole/gravity.list" >> /etc/dnsmasq.d/01-pihole.conf
|
||||
restartDNS
|
||||
# No configs were found
|
||||
case "${1}" in
|
||||
"web") echo 99;;
|
||||
*) echo -e " ${INFO} Pi-hole blocking will be enabled";;
|
||||
esac
|
||||
# Enable blocking
|
||||
pihole enable
|
||||
fi
|
||||
}
|
||||
|
||||
tailFunc() {
|
||||
echo "Press Ctrl-C to exit"
|
||||
tail -F /var/log/pihole.log
|
||||
# Warn user if Pi-hole's logging is disabled
|
||||
local logging_enabled=$(grep -c "^log-queries" /etc/dnsmasq.d/01-pihole.conf)
|
||||
if [[ "${logging_enabled}" == "0" ]]; then
|
||||
# No "log-queries" lines are found.
|
||||
# Commented out lines (such as "#log-queries") are ignored
|
||||
echo " ${CROSS} Warning: Query logging is disabled"
|
||||
fi
|
||||
echo -e " ${INFO} Press Ctrl-C to exit"
|
||||
|
||||
# Retrieve IPv4/6 addresses
|
||||
source /etc/pihole/setupVars.conf
|
||||
|
||||
# Strip date from each line
|
||||
# Colour blocklist/blacklist/wildcard entries as red
|
||||
# Colour A/AAAA/DHCP strings as white
|
||||
# Colour everything else as gray
|
||||
tail -f /var/log/pihole.log | sed -E \
|
||||
-e "s,($(date +'%b %d ')| dnsmasq[.*[0-9]]),,g" \
|
||||
-e "s,(.*(gravity.list|black.list|regex.list| config ).* is (0.0.0.0|::|NXDOMAIN|${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \
|
||||
-e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \
|
||||
-e "s,.*,${COL_GRAY}&${COL_NC},"
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -311,11 +330,13 @@ Switch Pi-hole subsystems to a different Github branch
|
||||
|
||||
Repositories:
|
||||
core [branch] Change the branch of Pi-hole's core subsystem
|
||||
web [branch] Change the branch of Admin Console subsystem
|
||||
web [branch] Change the branch of Web Interface subsystem
|
||||
ftl [branch] Change the branch of Pi-hole's FTL subsystem
|
||||
|
||||
Branches:
|
||||
master Update subsystems to the latest stable release
|
||||
dev Update subsystems to the latest development release"
|
||||
dev Update subsystems to the latest development release
|
||||
branchname Update subsystems to the specified branchname"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -326,12 +347,12 @@ Branches:
|
||||
|
||||
tricorderFunc() {
|
||||
if [[ ! -p "/dev/stdin" ]]; then
|
||||
echo "Please do not call Tricorder directly."
|
||||
echo -e " ${INFO} Please do not call Tricorder directly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! timeout 2 nc -z tricorder.pi-hole.net 9998 &> /dev/null; then
|
||||
echo "Unable to connect to Pi-hole's Tricorder server."
|
||||
if ! (echo > /dev/tcp/tricorder.pi-hole.net/9998) >/dev/null 2>&1; then
|
||||
echo -e " ${CROSS} Unable to connect to Pi-hole's Tricorder server"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -339,9 +360,10 @@ tricorderFunc() {
|
||||
openssl s_client -quiet -connect tricorder.pi-hole.net:9998 2> /dev/null < /dev/stdin
|
||||
exit "$?"
|
||||
else
|
||||
echo "Your debug log will be transmitted unencrypted via plain-text"
|
||||
echo "There is a possibility that this could be intercepted by a third party"
|
||||
echo "If you wish to cancel, press Ctrl-C to exit within 10 seconds"
|
||||
echo -e " ${INFO} ${COL_YELLOW}Security Notice${COL_NC}: ${COL_WHITE}openssl${COL_NC} is not installed
|
||||
Your debug log will be transmitted unencrypted via plain-text
|
||||
There is a possibility that this could be intercepted by a third party
|
||||
If you wish to cancel, press Ctrl-C to exit within 10 seconds"
|
||||
secs="10"
|
||||
while [[ "$secs" -gt "0" ]]; do
|
||||
echo -ne "."
|
||||
@@ -354,6 +376,11 @@ tricorderFunc() {
|
||||
fi
|
||||
}
|
||||
|
||||
updateCheckFunc() {
|
||||
"${PI_HOLE_SCRIPT_DIR}"/updatecheck.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
helpFunc() {
|
||||
echo "Usage: pihole [options]
|
||||
Example: 'pihole -w -h'
|
||||
@@ -362,7 +389,8 @@ Add '-h' after specific commands for more information on usage
|
||||
Whitelist/Blacklist Options:
|
||||
-w, whitelist Whitelist domain(s)
|
||||
-b, blacklist Blacklist domain(s)
|
||||
-wild, wildcard Blacklist domain(s), and all its subdomains
|
||||
--wild, wildcard Wildcard blacklist domain(s)
|
||||
--regex, regex Regex blacklist domains(s)
|
||||
Add '-h' for more info on whitelist/blacklist usage
|
||||
|
||||
Debugging Options:
|
||||
@@ -373,8 +401,8 @@ Debugging Options:
|
||||
-t, tail View the live output of the Pi-hole log
|
||||
|
||||
Options:
|
||||
-a, admin Admin Console options
|
||||
Add '-h' for more info on admin console usage
|
||||
-a, admin Web interface options
|
||||
Add '-h' for more info on Web Interface usage
|
||||
-c, chronometer Calculates stats and displays to an LCD
|
||||
Add '-h' for more info on chronometer usage
|
||||
-g, updateGravity Update the list of ad-serving domains
|
||||
@@ -382,9 +410,10 @@ Options:
|
||||
-l, logging Specify whether the Pi-hole log should be used
|
||||
Add '-h' for more info on logging usage
|
||||
-q, query Query the adlists for a specified domain
|
||||
Add '-exact' AFTER a specified domain for exact match
|
||||
Add '-h' for more info on query usage
|
||||
-up, updatePihole Update Pi-hole subsystems
|
||||
-v, version Show installed versions of Pi-hole, Admin Console & FTL
|
||||
Add '--check-only' to exit script before update is performed.
|
||||
-v, version Show installed versions of Pi-hole, Web Interface & FTL
|
||||
Add '-h' for more info on version usage
|
||||
uninstall Uninstall Pi-hole from your system
|
||||
status Display the running status of Pi-hole subsystems
|
||||
@@ -403,12 +432,13 @@ fi
|
||||
|
||||
# Handle redirecting to specific functions based on arguments
|
||||
case "${1}" in
|
||||
"-w" | "whitelist" ) whitelistFunc "$@";;
|
||||
"-b" | "blacklist" ) blacklistFunc "$@";;
|
||||
"-wild" | "wildcard" ) wildcardFunc "$@";;
|
||||
"-w" | "whitelist" ) listFunc "$@";;
|
||||
"-b" | "blacklist" ) listFunc "$@";;
|
||||
"--wild" | "wildcard" ) listFunc "$@";;
|
||||
"--regex" | "regex" ) listFunc "$@";;
|
||||
"-d" | "debug" ) debugFunc "$@";;
|
||||
"-f" | "flush" ) flushFunc "$@";;
|
||||
"-up" | "updatePihole" ) updatePiholeFunc;;
|
||||
"-up" | "updatePihole" ) updatePiholeFunc "$@";;
|
||||
"-r" | "reconfigure" ) reconfigurePiholeFunc;;
|
||||
"-g" | "updateGravity" ) updateGravityFunc "$@";;
|
||||
"-c" | "chronometer" ) chronometerFunc "$@";;
|
||||
@@ -419,11 +449,12 @@ case "${1}" in
|
||||
"uninstall" ) uninstallFunc;;
|
||||
"enable" ) piholeEnable 1;;
|
||||
"disable" ) piholeEnable 0 "$2";;
|
||||
"status" ) piholeStatus "$2";;
|
||||
"restartdns" ) restartDNS;;
|
||||
"status" ) statusFunc "$2";;
|
||||
"restartdns" ) restartDNS "$2";;
|
||||
"-a" | "admin" ) webpageFunc "$@";;
|
||||
"-t" | "tail" ) tailFunc;;
|
||||
"checkout" ) piholeCheckoutFunc "$@";;
|
||||
"tricorder" ) tricorderFunc;;
|
||||
"updatechecker" ) updateCheckFunc "$@";;
|
||||
* ) helpFunc;;
|
||||
esac
|
||||
|
@@ -3,3 +3,4 @@ pytest
|
||||
pytest-xdist
|
||||
pytest-cov
|
||||
testinfra
|
||||
tox
|
||||
|
6
setup.py
Normal file
6
setup.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
setup_requires=['pytest-runner'],
|
||||
tests_require=['pytest'],
|
||||
)
|
25
test/README.md
Normal file
25
test/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Recommended way to run tests
|
||||
|
||||
Make sure you have Docker and Python w/pip package manager.
|
||||
|
||||
From command line all you need to do is:
|
||||
|
||||
- `pip install tox`
|
||||
- `tox`
|
||||
|
||||
Tox handles setting up a virtual environment for python dependancies, installing dependancies, building the docker images used by tests, and finally running tests. It's an easy way to have travis-ci like build behavior locally.
|
||||
|
||||
## Alternative py.test method of running tests
|
||||
|
||||
You're responsible for setting up your virtual env and dependancies in this situation.
|
||||
|
||||
```
|
||||
py.test -vv -n auto -m "build_stage"
|
||||
py.test -vv -n auto -m "not build_stage"
|
||||
```
|
||||
|
||||
The build_stage tests have to run first to create the docker images, followed by the actual tests which utilize said images. Unless you're changing your dockerfiles you shouldn't have to run the build_stage every time - but it's a good idea to rebuild at least once a day in case the base Docker images or packages change.
|
||||
|
||||
# How do I debug python?
|
||||
|
||||
Highly recommended: Setup PyCharm on a **Docker enabled** machine. Having a python debugger like PyCharm changes your life if you've never used it :)
|
113
test/conftest.py
113
test/conftest.py
@@ -1,14 +1,30 @@
|
||||
import pytest
|
||||
import testinfra
|
||||
from textwrap import dedent
|
||||
|
||||
check_output = testinfra.get_backend(
|
||||
"local://"
|
||||
).get_module("Command").check_output
|
||||
|
||||
SETUPVARS = {
|
||||
'PIHOLE_INTERFACE': 'eth99',
|
||||
'IPV4_ADDRESS': '1.1.1.1',
|
||||
'IPV6_ADDRESS': 'FE80::240:D0FF:FE48:4672',
|
||||
'PIHOLE_DNS_1': '4.2.2.1',
|
||||
'PIHOLE_DNS_2': '4.2.2.2'
|
||||
}
|
||||
|
||||
tick_box = "[\x1b[1;32m\xe2\x9c\x93\x1b[0m]".decode("utf-8")
|
||||
cross_box = "[\x1b[1;31m\xe2\x9c\x97\x1b[0m]".decode("utf-8")
|
||||
info_box = "[i]".decode("utf-8")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def Pihole(Docker):
|
||||
''' used to contain some script stubbing, now pretty much an alias.
|
||||
Also provides bash as the default run function shell '''
|
||||
'''
|
||||
used to contain some script stubbing, now pretty much an alias.
|
||||
Also provides bash as the default run function shell
|
||||
'''
|
||||
def run_bash(self, command, *args, **kwargs):
|
||||
cmd = self.get_command(command, *args)
|
||||
if self.user is not None:
|
||||
@@ -22,12 +38,18 @@ def Pihole(Docker):
|
||||
return out
|
||||
|
||||
funcType = type(Docker.run)
|
||||
Docker.run = funcType(run_bash, Docker, testinfra.backend.docker.DockerBackend)
|
||||
Docker.run = funcType(run_bash,
|
||||
Docker,
|
||||
testinfra.backend.docker.DockerBackend)
|
||||
return Docker
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def Docker(request, args, image, cmd):
|
||||
''' combine our fixtures into a docker run command and setup finalizer to cleanup '''
|
||||
'''
|
||||
combine our fixtures into a docker run command and setup finalizer to
|
||||
cleanup
|
||||
'''
|
||||
assert 'docker' in check_output('id'), "Are you in the docker group?"
|
||||
docker_run = "docker run {} {} {}".format(args, image, cmd)
|
||||
docker_id = check_output(docker_run)
|
||||
@@ -40,22 +62,95 @@ def Docker(request, args, image, cmd):
|
||||
docker_container.id = docker_id
|
||||
return docker_container
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def args(request):
|
||||
''' -t became required when tput began being used '''
|
||||
'''
|
||||
-t became required when tput began being used
|
||||
'''
|
||||
return '-t -d'
|
||||
|
||||
@pytest.fixture(params=['debian', 'centos'])
|
||||
|
||||
@pytest.fixture(params=['debian', 'centos', 'fedora'])
|
||||
def tag(request):
|
||||
''' consumed by image to make the test matrix '''
|
||||
'''
|
||||
consumed by image to make the test matrix
|
||||
'''
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def image(request, tag):
|
||||
''' built by test_000_build_containers.py '''
|
||||
'''
|
||||
built by test_000_build_containers.py
|
||||
'''
|
||||
return 'pytest_pihole:{}'.format(tag)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def cmd(request):
|
||||
''' default to doing nothing by tailing null, but don't exit '''
|
||||
'''
|
||||
default to doing nothing by tailing null, but don't exit
|
||||
'''
|
||||
return 'tail -f /dev/null'
|
||||
|
||||
|
||||
# Helper functions
|
||||
def mock_command(script, args, container):
|
||||
'''
|
||||
Allows for setup of commands we don't really want to have to run for real
|
||||
in unit tests
|
||||
'''
|
||||
full_script_path = '/usr/local/bin/{}'.format(script)
|
||||
mock_script = dedent('''\
|
||||
#!/bin/bash -e
|
||||
echo "\$0 \$@" >> /var/log/{script}
|
||||
case "\$1" in'''.format(script=script))
|
||||
for k, v in args.iteritems():
|
||||
case = dedent('''
|
||||
{arg})
|
||||
echo {res}
|
||||
exit {retcode}
|
||||
;;'''.format(arg=k, res=v[0], retcode=v[1]))
|
||||
mock_script += case
|
||||
mock_script += dedent('''
|
||||
esac''')
|
||||
container.run('''
|
||||
cat <<EOF> {script}\n{content}\nEOF
|
||||
chmod +x {script}
|
||||
rm -f /var/log/{scriptlog}'''.format(script=full_script_path,
|
||||
content=mock_script,
|
||||
scriptlog=script))
|
||||
|
||||
|
||||
def mock_command_2(script, args, container):
|
||||
'''
|
||||
Allows for setup of commands we don't really want to have to run for real
|
||||
in unit tests
|
||||
'''
|
||||
full_script_path = '/usr/local/bin/{}'.format(script)
|
||||
mock_script = dedent('''\
|
||||
#!/bin/bash -e
|
||||
echo "\$0 \$@" >> /var/log/{script}
|
||||
case "\$1 \$2" in'''.format(script=script))
|
||||
for k, v in args.iteritems():
|
||||
case = dedent('''
|
||||
\"{arg}\")
|
||||
echo \"{res}\"
|
||||
exit {retcode}
|
||||
;;'''.format(arg=k, res=v[0], retcode=v[1]))
|
||||
mock_script += case
|
||||
mock_script += dedent('''
|
||||
esac''')
|
||||
container.run('''
|
||||
cat <<EOF> {script}\n{content}\nEOF
|
||||
chmod +x {script}
|
||||
rm -f /var/log/{scriptlog}'''.format(script=full_script_path,
|
||||
content=mock_script,
|
||||
scriptlog=script))
|
||||
|
||||
|
||||
def run_script(Pihole, script):
|
||||
result = Pihole.run(script)
|
||||
assert result.rc == 0
|
||||
return result
|
||||
|
16
test/fedora.Dockerfile
Normal file
16
test/fedora.Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM fedora:latest
|
||||
|
||||
ENV GITDIR /etc/.pihole
|
||||
ENV SCRIPTDIR /opt/pihole
|
||||
|
||||
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
|
||||
ADD . $GITDIR
|
||||
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
|
||||
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ENV PH_TEST true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
@@ -6,10 +6,15 @@ run_local = testinfra.get_backend(
|
||||
"local://"
|
||||
).get_module("Command").run
|
||||
|
||||
|
||||
@pytest.mark.parametrize("image,tag", [
|
||||
( 'test/debian.Dockerfile', 'pytest_pihole:debian' ),
|
||||
( 'test/centos.Dockerfile', 'pytest_pihole:centos' ),
|
||||
('test/debian.Dockerfile', 'pytest_pihole:debian'),
|
||||
('test/centos.Dockerfile', 'pytest_pihole:centos'),
|
||||
('test/fedora.Dockerfile', 'pytest_pihole:fedora'),
|
||||
])
|
||||
# mark as 'build_stage' so we can ensure images are build first when tests
|
||||
# are executed in parallel. (not required when tests are executed serially)
|
||||
@pytest.mark.build_stage
|
||||
def test_build_pihole_image(image, tag):
|
||||
build_cmd = run_local('docker build -f {} -t {} .'.format(image, tag))
|
||||
if build_cmd.rc != 0:
|
||||
|
@@ -1,20 +1,40 @@
|
||||
import pytest
|
||||
from textwrap import dedent
|
||||
import re
|
||||
from conftest import (
|
||||
SETUPVARS,
|
||||
tick_box,
|
||||
info_box,
|
||||
cross_box,
|
||||
mock_command,
|
||||
mock_command_2,
|
||||
run_script
|
||||
)
|
||||
|
||||
|
||||
def test_supported_operating_system(Pihole):
|
||||
'''
|
||||
confirm installer exists on unsupported distribution
|
||||
'''
|
||||
# break supported package managers to emulate an unsupported distribution
|
||||
Pihole.run('rm -rf /usr/bin/apt-get')
|
||||
Pihole.run('rm -rf /usr/bin/rpm')
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
''')
|
||||
expected_stdout = cross_box + ' OS distribution not supported'
|
||||
assert expected_stdout in distro_check.stdout
|
||||
# assert distro_check.rc == 1
|
||||
|
||||
SETUPVARS = {
|
||||
'PIHOLE_INTERFACE' : 'eth99',
|
||||
'IPV4_ADDRESS' : '1.1.1.1',
|
||||
'IPV6_ADDRESS' : 'FE80::240:D0FF:FE48:4672',
|
||||
'PIHOLE_DNS_1' : '4.2.2.1',
|
||||
'PIHOLE_DNS_2' : '4.2.2.2'
|
||||
}
|
||||
|
||||
def test_setupVars_are_sourced_to_global_scope(Pihole):
|
||||
''' currently update_dialogs sources setupVars with a dot,
|
||||
'''
|
||||
currently update_dialogs sources setupVars with a dot,
|
||||
then various other functions use the variables.
|
||||
This confirms the sourced variables are in scope between functions '''
|
||||
This confirms the sourced variables are in scope between functions
|
||||
'''
|
||||
setup_var_file = 'cat <<EOF> /etc/pihole/setupVars.conf\n'
|
||||
for k,v in SETUPVARS.iteritems():
|
||||
for k, v in SETUPVARS.iteritems():
|
||||
setup_var_file += "{}={}\n".format(k, v)
|
||||
setup_var_file += "EOF\n"
|
||||
Pihole.run(setup_var_file)
|
||||
@@ -39,13 +59,17 @@ def test_setupVars_are_sourced_to_global_scope(Pihole):
|
||||
|
||||
output = run_script(Pihole, script).stdout
|
||||
|
||||
for k,v in SETUPVARS.iteritems():
|
||||
for k, v in SETUPVARS.iteritems():
|
||||
assert "{}={}".format(k, v) in output
|
||||
|
||||
|
||||
def test_setupVars_saved_to_file(Pihole):
|
||||
''' confirm saved settings are written to a file for future updates to re-use '''
|
||||
set_setup_vars = '\n' # dedent works better with this and padding matching script below
|
||||
for k,v in SETUPVARS.iteritems():
|
||||
'''
|
||||
confirm saved settings are written to a file for future updates to re-use
|
||||
'''
|
||||
# dedent works better with this and padding matching script below
|
||||
set_setup_vars = '\n'
|
||||
for k, v in SETUPVARS.iteritems():
|
||||
set_setup_vars += " {}={}\n".format(k, v)
|
||||
Pihole.run(set_setup_vars).stdout
|
||||
|
||||
@@ -55,49 +79,66 @@ def test_setupVars_saved_to_file(Pihole):
|
||||
TERM=xterm
|
||||
source /opt/pihole/basic-install.sh
|
||||
{}
|
||||
mkdir -p /etc/dnsmasq.d
|
||||
version_check_dnsmasq
|
||||
echo "" > /etc/pihole/pihole-FTL.conf
|
||||
finalExports
|
||||
cat /etc/pihole/setupVars.conf
|
||||
'''.format(set_setup_vars))
|
||||
|
||||
output = run_script(Pihole, script).stdout
|
||||
|
||||
for k,v in SETUPVARS.iteritems():
|
||||
for k, v in SETUPVARS.iteritems():
|
||||
assert "{}={}".format(k, v) in output
|
||||
|
||||
|
||||
def test_configureFirewall_firewalld_running_no_errors(Pihole):
|
||||
''' confirms firewalld rules are applied when firewallD is running '''
|
||||
'''
|
||||
confirms firewalld rules are applied when firewallD is running
|
||||
'''
|
||||
# firewallD returns 'running' as status
|
||||
mock_command('firewall-cmd', {'*':('running', 0)}, Pihole)
|
||||
mock_command('firewall-cmd', {'*': ('running', 0)}, Pihole)
|
||||
# Whiptail dialog returns Ok for user prompt
|
||||
mock_command('whiptail', {'*':('', 0)}, Pihole)
|
||||
mock_command('whiptail', {'*': ('', 0)}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
''')
|
||||
expected_stdout = 'Configuring FirewallD for httpd and dnsmasq.'
|
||||
expected_stdout = 'Configuring FirewallD for httpd and pihole-FTL'
|
||||
assert expected_stdout in configureFirewall.stdout
|
||||
firewall_calls = Pihole.run('cat /var/log/firewall-cmd').stdout
|
||||
assert 'firewall-cmd --state' in firewall_calls
|
||||
assert 'firewall-cmd --permanent --add-service=http --add-service=dns' in firewall_calls
|
||||
assert ('firewall-cmd '
|
||||
'--permanent '
|
||||
'--add-service=http '
|
||||
'--add-service=dns') in firewall_calls
|
||||
assert 'firewall-cmd --reload' in firewall_calls
|
||||
|
||||
|
||||
def test_configureFirewall_firewalld_disabled_no_errors(Pihole):
|
||||
''' confirms firewalld rules are not applied when firewallD is not running '''
|
||||
'''
|
||||
confirms firewalld rules are not applied when firewallD is not running
|
||||
'''
|
||||
# firewallD returns non-running status
|
||||
mock_command('firewall-cmd', {'*':('not running', '1')}, Pihole)
|
||||
mock_command('firewall-cmd', {'*': ('not running', '1')}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
''')
|
||||
expected_stdout = 'No active firewall detected.. skipping firewall configuration.'
|
||||
expected_stdout = ('No active firewall detected.. '
|
||||
'skipping firewall configuration')
|
||||
assert expected_stdout in configureFirewall.stdout
|
||||
|
||||
|
||||
def test_configureFirewall_firewalld_enabled_declined_no_errors(Pihole):
|
||||
''' confirms firewalld rules are not applied when firewallD is running, user declines ruleset '''
|
||||
'''
|
||||
confirms firewalld rules are not applied when firewallD is running, user
|
||||
declines ruleset
|
||||
'''
|
||||
# firewallD returns running status
|
||||
mock_command('firewall-cmd', {'*':('running', 0)}, Pihole)
|
||||
mock_command('firewall-cmd', {'*': ('running', 0)}, Pihole)
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*':('', 1)}, Pihole)
|
||||
mock_command('whiptail', {'*': ('', 1)}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
@@ -105,6 +146,7 @@ def test_configureFirewall_firewalld_enabled_declined_no_errors(Pihole):
|
||||
expected_stdout = 'Not installing firewall rulesets.'
|
||||
assert expected_stdout in configureFirewall.stdout
|
||||
|
||||
|
||||
def test_configureFirewall_no_firewall(Pihole):
|
||||
''' confirms firewall skipped no daemon is running '''
|
||||
configureFirewall = Pihole.run('''
|
||||
@@ -114,14 +156,18 @@ def test_configureFirewall_no_firewall(Pihole):
|
||||
expected_stdout = 'No active firewall detected'
|
||||
assert expected_stdout in configureFirewall.stdout
|
||||
|
||||
|
||||
def test_configureFirewall_IPTables_enabled_declined_no_errors(Pihole):
|
||||
''' confirms IPTables rules are not applied when IPTables is running, user declines ruleset '''
|
||||
'''
|
||||
confirms IPTables rules are not applied when IPTables is running, user
|
||||
declines ruleset
|
||||
'''
|
||||
# iptables command exists
|
||||
mock_command('iptables', {'*':('', '0')}, Pihole)
|
||||
mock_command('iptables', {'*': ('', '0')}, Pihole)
|
||||
# modinfo returns always true (ip_tables module check)
|
||||
mock_command('modinfo', {'*':('', '0')}, Pihole)
|
||||
mock_command('modinfo', {'*': ('', '0')}, Pihole)
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*':('', '1')}, Pihole)
|
||||
mock_command('whiptail', {'*': ('', '1')}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
@@ -129,14 +175,19 @@ def test_configureFirewall_IPTables_enabled_declined_no_errors(Pihole):
|
||||
expected_stdout = 'Not installing firewall rulesets.'
|
||||
assert expected_stdout in configureFirewall.stdout
|
||||
|
||||
|
||||
def test_configureFirewall_IPTables_enabled_rules_exist_no_errors(Pihole):
|
||||
''' confirms IPTables rules are not applied when IPTables is running and rules exist '''
|
||||
# iptables command exists and returns 0 on calls (should return 0 on iptables -C)
|
||||
mock_command('iptables', {'-S':('-P INPUT DENY', '0')}, Pihole)
|
||||
'''
|
||||
confirms IPTables rules are not applied when IPTables is running and rules
|
||||
exist
|
||||
'''
|
||||
# iptables command exists and returns 0 on calls
|
||||
# (should return 0 on iptables -C)
|
||||
mock_command('iptables', {'-S': ('-P INPUT DENY', '0')}, Pihole)
|
||||
# modinfo returns always true (ip_tables module check)
|
||||
mock_command('modinfo', {'*':('', '0')}, Pihole)
|
||||
mock_command('modinfo', {'*': ('', '0')}, Pihole)
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*':('', '0')}, Pihole)
|
||||
mock_command('whiptail', {'*': ('', '0')}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
@@ -144,18 +195,46 @@ def test_configureFirewall_IPTables_enabled_rules_exist_no_errors(Pihole):
|
||||
expected_stdout = 'Installing new IPTables firewall rulesets'
|
||||
assert expected_stdout in configureFirewall.stdout
|
||||
firewall_calls = Pihole.run('cat /var/log/iptables').stdout
|
||||
assert 'iptables -I INPUT 1 -p tcp -m tcp --dport 80 -j ACCEPT' not in firewall_calls
|
||||
assert 'iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT' not in firewall_calls
|
||||
assert 'iptables -I INPUT 1 -p udp -m udp --dport 53 -j ACCEPT' not in firewall_calls
|
||||
# General call type occurances
|
||||
assert len(re.findall(r'iptables -S', firewall_calls)) == 1
|
||||
assert len(re.findall(r'iptables -C', firewall_calls)) == 4
|
||||
assert len(re.findall(r'iptables -I', firewall_calls)) == 0
|
||||
|
||||
# Specific port call occurances
|
||||
assert len(re.findall(r'tcp --dport 80', firewall_calls)) == 1
|
||||
assert len(re.findall(r'tcp --dport 53', firewall_calls)) == 1
|
||||
assert len(re.findall(r'udp --dport 53', firewall_calls)) == 1
|
||||
assert len(re.findall(r'tcp --dport 4711:4720', firewall_calls)) == 1
|
||||
|
||||
|
||||
def test_configureFirewall_IPTables_enabled_not_exist_no_errors(Pihole):
|
||||
''' confirms IPTables rules are applied when IPTables is running and rules do not exist '''
|
||||
'''
|
||||
confirms IPTables rules are applied when IPTables is running and rules do
|
||||
not exist
|
||||
'''
|
||||
# iptables command and returns 0 on calls (should return 1 on iptables -C)
|
||||
mock_command('iptables', {'-S':('-P INPUT DENY', '0'), '-C':('', 1), '-I':('', 0)}, Pihole)
|
||||
mock_command(
|
||||
'iptables',
|
||||
{
|
||||
'-S': (
|
||||
'-P INPUT DENY',
|
||||
'0'
|
||||
),
|
||||
'-C': (
|
||||
'',
|
||||
1
|
||||
),
|
||||
'-I': (
|
||||
'',
|
||||
0
|
||||
)
|
||||
},
|
||||
Pihole
|
||||
)
|
||||
# modinfo returns always true (ip_tables module check)
|
||||
mock_command('modinfo', {'*':('', '0')}, Pihole)
|
||||
mock_command('modinfo', {'*': ('', '0')}, Pihole)
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*':('', '0')}, Pihole)
|
||||
mock_command('whiptail', {'*': ('', '0')}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
@@ -163,192 +242,233 @@ def test_configureFirewall_IPTables_enabled_not_exist_no_errors(Pihole):
|
||||
expected_stdout = 'Installing new IPTables firewall rulesets'
|
||||
assert expected_stdout in configureFirewall.stdout
|
||||
firewall_calls = Pihole.run('cat /var/log/iptables').stdout
|
||||
assert 'iptables -I INPUT 1 -p tcp -m tcp --dport 80 -j ACCEPT' in firewall_calls
|
||||
assert 'iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT' in firewall_calls
|
||||
assert 'iptables -I INPUT 1 -p udp -m udp --dport 53 -j ACCEPT' in firewall_calls
|
||||
# General call type occurances
|
||||
assert len(re.findall(r'iptables -S', firewall_calls)) == 1
|
||||
assert len(re.findall(r'iptables -C', firewall_calls)) == 4
|
||||
assert len(re.findall(r'iptables -I', firewall_calls)) == 4
|
||||
|
||||
# Specific port call occurances
|
||||
assert len(re.findall(r'tcp --dport 80', firewall_calls)) == 2
|
||||
assert len(re.findall(r'tcp --dport 53', firewall_calls)) == 2
|
||||
assert len(re.findall(r'udp --dport 53', firewall_calls)) == 2
|
||||
assert len(re.findall(r'tcp --dport 4711:4720', firewall_calls)) == 2
|
||||
|
||||
|
||||
def test_selinux_enforcing_default_exit(Pihole):
|
||||
'''
|
||||
confirms installer prompts to exit when SELinux is Enforcing by default
|
||||
'''
|
||||
# getenforce returns the running state of SELinux
|
||||
mock_command('getenforce', {'*': ('Enforcing', '0')}, Pihole)
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*': ('', '1')}, Pihole)
|
||||
check_selinux = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
''')
|
||||
expected_stdout = info_box + ' SELinux mode detected: Enforcing'
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
expected_stdout = 'SELinux Enforcing detected, exiting installer'
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
assert check_selinux.rc == 1
|
||||
|
||||
|
||||
def test_selinux_enforcing_continue(Pihole):
|
||||
'''
|
||||
confirms installer prompts to continue with custom policy warning
|
||||
'''
|
||||
# getenforce returns the running state of SELinux
|
||||
mock_command('getenforce', {'*': ('Enforcing', '0')}, Pihole)
|
||||
# Whiptail dialog returns Continue for user prompt
|
||||
mock_command('whiptail', {'*': ('', '0')}, Pihole)
|
||||
check_selinux = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
''')
|
||||
expected_stdout = info_box + ' SELinux mode detected: Enforcing'
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
expected_stdout = info_box + (' Continuing installation with SELinux '
|
||||
'Enforcing')
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
expected_stdout = info_box + (' Please refer to official SELinux '
|
||||
'documentation to create a custom policy')
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
assert check_selinux.rc == 0
|
||||
|
||||
|
||||
def test_selinux_permissive(Pihole):
|
||||
'''
|
||||
confirms installer continues when SELinux is Permissive
|
||||
'''
|
||||
# getenforce returns the running state of SELinux
|
||||
mock_command('getenforce', {'*': ('Permissive', '0')}, Pihole)
|
||||
check_selinux = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
''')
|
||||
expected_stdout = info_box + ' SELinux mode detected: Permissive'
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
assert check_selinux.rc == 0
|
||||
|
||||
|
||||
def test_selinux_disabled(Pihole):
|
||||
'''
|
||||
confirms installer continues when SELinux is Disabled
|
||||
'''
|
||||
mock_command('getenforce', {'*': ('Disabled', '0')}, Pihole)
|
||||
check_selinux = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
''')
|
||||
expected_stdout = info_box + ' SELinux mode detected: Disabled'
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
assert check_selinux.rc == 0
|
||||
|
||||
|
||||
def test_installPiholeWeb_fresh_install_no_errors(Pihole):
|
||||
''' confirms all web page assets from Core repo are installed on a fresh build '''
|
||||
'''
|
||||
confirms all web page assets from Core repo are installed on a fresh build
|
||||
'''
|
||||
installWeb = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
installPiholeWeb
|
||||
''')
|
||||
assert 'Installing pihole custom index page...' in installWeb.stdout
|
||||
assert 'No default index.lighttpd.html file found... not backing up' in installWeb.stdout
|
||||
expected_stdout = info_box + ' Installing blocking page...'
|
||||
assert expected_stdout in installWeb.stdout
|
||||
expected_stdout = tick_box + (' Creating directory for blocking page, '
|
||||
'and copying files')
|
||||
assert expected_stdout in installWeb.stdout
|
||||
expected_stdout = cross_box + ' Backing up index.lighttpd.html'
|
||||
assert expected_stdout in installWeb.stdout
|
||||
expected_stdout = ('No default index.lighttpd.html file found... '
|
||||
'not backing up')
|
||||
assert expected_stdout in installWeb.stdout
|
||||
expected_stdout = tick_box + ' Installing sudoer file'
|
||||
assert expected_stdout in installWeb.stdout
|
||||
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
|
||||
assert 'index.php' in web_directory
|
||||
assert 'index.js' in web_directory
|
||||
assert 'blockingpage.css' in web_directory
|
||||
|
||||
def test_installPiholeWeb_empty_directory_no_errors(Pihole):
|
||||
''' confirms all web page assets from Core repo are installed in an emtpy directory '''
|
||||
installWeb = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
mkdir -p /var/www/html/pihole
|
||||
installPiholeWeb
|
||||
''')
|
||||
assert 'Installing pihole custom index page...' in installWeb.stdout
|
||||
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
|
||||
assert 'index.php missing, replacing...' in installWeb.stdout
|
||||
assert 'index.js missing, replacing...' in installWeb.stdout
|
||||
assert 'blockingpage.css missing, replacing...' in installWeb.stdout
|
||||
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
|
||||
assert 'index.php' in web_directory
|
||||
assert 'index.js' in web_directory
|
||||
assert 'blockingpage.css' in web_directory
|
||||
|
||||
def test_installPiholeWeb_index_php_no_errors(Pihole):
|
||||
''' confirms all web page assets from Core repo are installed when necessary '''
|
||||
installWeb = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
mkdir -p /var/www/html/pihole
|
||||
touch /var/www/html/pihole/index.php
|
||||
installPiholeWeb
|
||||
''')
|
||||
assert 'Installing pihole custom index page...' in installWeb.stdout
|
||||
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
|
||||
assert 'Existing index.php detected, not overwriting' in installWeb.stdout
|
||||
assert 'index.js missing, replacing...' in installWeb.stdout
|
||||
assert 'blockingpage.css missing, replacing...' in installWeb.stdout
|
||||
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
|
||||
assert 'index.php' in web_directory
|
||||
assert 'index.js' in web_directory
|
||||
assert 'blockingpage.css' in web_directory
|
||||
|
||||
def test_installPiholeWeb_index_js_no_errors(Pihole):
|
||||
''' confirms all web page assets from Core repo are installed when necessary '''
|
||||
installWeb = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
mkdir -p /var/www/html/pihole
|
||||
touch /var/www/html/pihole/index.js
|
||||
installPiholeWeb
|
||||
''')
|
||||
assert 'Installing pihole custom index page...' in installWeb.stdout
|
||||
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
|
||||
assert 'index.php missing, replacing...' in installWeb.stdout
|
||||
assert 'Existing index.js detected, not overwriting' in installWeb.stdout
|
||||
assert 'blockingpage.css missing, replacing...' in installWeb.stdout
|
||||
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
|
||||
assert 'index.php' in web_directory
|
||||
assert 'index.js' in web_directory
|
||||
assert 'blockingpage.css' in web_directory
|
||||
|
||||
def test_installPiholeWeb_blockingpage_css_no_errors(Pihole):
|
||||
''' confirms all web page assets from Core repo are installed when necessary '''
|
||||
installWeb = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
mkdir -p /var/www/html/pihole
|
||||
touch /var/www/html/pihole/blockingpage.css
|
||||
installPiholeWeb
|
||||
''')
|
||||
assert 'Installing pihole custom index page...' in installWeb.stdout
|
||||
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
|
||||
assert 'index.php missing, replacing...' in installWeb.stdout
|
||||
assert 'index.js missing, replacing...' in installWeb.stdout
|
||||
assert 'Existing blockingpage.css detected, not overwriting' in installWeb.stdout
|
||||
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
|
||||
assert 'index.php' in web_directory
|
||||
assert 'index.js' in web_directory
|
||||
assert 'blockingpage.css' in web_directory
|
||||
|
||||
def test_installPiholeWeb_already_populated_no_errors(Pihole):
|
||||
''' confirms all web page assets from Core repo are installed when necessary '''
|
||||
installWeb = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
mkdir -p /var/www/html/pihole
|
||||
touch /var/www/html/pihole/index.php
|
||||
touch /var/www/html/pihole/index.js
|
||||
touch /var/www/html/pihole/blockingpage.css
|
||||
installPiholeWeb
|
||||
''')
|
||||
assert 'Installing pihole custom index page...' in installWeb.stdout
|
||||
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
|
||||
assert 'Existing index.php detected, not overwriting' in installWeb.stdout
|
||||
assert 'index.php missing, replacing...' not in installWeb.stdout
|
||||
assert 'Existing index.js detected, not overwriting' in installWeb.stdout
|
||||
assert 'index.js missing, replacing...' not in installWeb.stdout
|
||||
assert 'Existing blockingpage.css detected, not overwriting' in installWeb.stdout
|
||||
assert 'blockingpage.css missing, replacing... ' not in installWeb.stdout
|
||||
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
|
||||
assert 'index.php' in web_directory
|
||||
assert 'index.js' in web_directory
|
||||
assert 'blockingpage.css' in web_directory
|
||||
|
||||
def test_update_package_cache_success_no_errors(Pihole):
|
||||
''' confirms package cache was updated without any errors'''
|
||||
'''
|
||||
confirms package cache was updated without any errors
|
||||
'''
|
||||
updateCache = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
update_package_cache
|
||||
''')
|
||||
assert 'Updating local cache of available packages...' in updateCache.stdout
|
||||
assert 'ERROR' not in updateCache.stdout
|
||||
assert 'done!' in updateCache.stdout
|
||||
expected_stdout = tick_box + ' Update local cache of available packages'
|
||||
assert expected_stdout in updateCache.stdout
|
||||
assert 'error' not in updateCache.stdout.lower()
|
||||
|
||||
|
||||
def test_update_package_cache_failure_no_errors(Pihole):
|
||||
''' confirms package cache was not updated'''
|
||||
mock_command('apt-get', {'update':('', '1')}, Pihole)
|
||||
'''
|
||||
confirms package cache was not updated
|
||||
'''
|
||||
mock_command('apt-get', {'update': ('', '1')}, Pihole)
|
||||
updateCache = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
update_package_cache
|
||||
''')
|
||||
assert 'Updating local cache of available packages...' in updateCache.stdout
|
||||
assert 'ERROR' in updateCache.stdout
|
||||
assert 'done!' not in updateCache.stdout
|
||||
expected_stdout = cross_box + ' Update local cache of available packages'
|
||||
assert expected_stdout in updateCache.stdout
|
||||
assert 'Error: Unable to update package cache.' in updateCache.stdout
|
||||
|
||||
|
||||
def test_FTL_detect_aarch64_no_errors(Pihole):
|
||||
''' confirms only aarch64 package is downloaded for FTL engine '''
|
||||
'''
|
||||
confirms only aarch64 package is downloaded for FTL engine
|
||||
'''
|
||||
# mock uname to return aarch64 platform
|
||||
mock_command('uname', {'-m':('aarch64', '0')}, Pihole)
|
||||
mock_command('uname', {'-m': ('aarch64', '0')}, Pihole)
|
||||
# mock ldd to respond with aarch64 shared library
|
||||
mock_command('ldd', {'/bin/ls':('/lib/ld-linux-aarch64.so.1', '0')}, Pihole)
|
||||
mock_command(
|
||||
'ldd',
|
||||
{
|
||||
'/bin/ls': (
|
||||
'/lib/ld-linux-aarch64.so.1',
|
||||
'0'
|
||||
)
|
||||
},
|
||||
Pihole
|
||||
)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
''')
|
||||
expected_stdout = 'Detected ARM-aarch64 architecture'
|
||||
expected_stdout = info_box + ' FTL Checks...'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + ' Detected ARM-aarch64 architecture'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + ' Downloading and Installing FTL'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_FTL_detect_armv6l_no_errors(Pihole):
|
||||
''' confirms only armv6l package is downloaded for FTL engine '''
|
||||
'''
|
||||
confirms only armv6l package is downloaded for FTL engine
|
||||
'''
|
||||
# mock uname to return armv6l platform
|
||||
mock_command('uname', {'-m':('armv6l', '0')}, Pihole)
|
||||
mock_command('uname', {'-m': ('armv6l', '0')}, Pihole)
|
||||
# mock ldd to respond with aarch64 shared library
|
||||
mock_command('ldd', {'/bin/ls':('/lib/ld-linux-armhf.so.3', '0')}, Pihole)
|
||||
mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
''')
|
||||
expected_stdout = 'Detected ARM-hf architecture (armv6 or lower)'
|
||||
expected_stdout = info_box + ' FTL Checks...'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + (' Detected ARM-hf architecture '
|
||||
'(armv6 or lower)')
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + ' Downloading and Installing FTL'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_FTL_detect_armv7l_no_errors(Pihole):
|
||||
''' confirms only armv7l package is downloaded for FTL engine '''
|
||||
'''
|
||||
confirms only armv7l package is downloaded for FTL engine
|
||||
'''
|
||||
# mock uname to return armv7l platform
|
||||
mock_command('uname', {'-m':('armv7l', '0')}, Pihole)
|
||||
mock_command('uname', {'-m': ('armv7l', '0')}, Pihole)
|
||||
# mock ldd to respond with aarch64 shared library
|
||||
mock_command('ldd', {'/bin/ls':('/lib/ld-linux-armhf.so.3', '0')}, Pihole)
|
||||
mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
''')
|
||||
expected_stdout = 'Detected ARM-hf architecture (armv7+)'
|
||||
expected_stdout = info_box + ' FTL Checks...'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + ' Detected ARM-hf architecture (armv7+)'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + ' Downloading and Installing FTL'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_FTL_detect_x86_64_no_errors(Pihole):
|
||||
''' confirms only x86_64 package is downloaded for FTL engine '''
|
||||
'''
|
||||
confirms only x86_64 package is downloaded for FTL engine
|
||||
'''
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
''')
|
||||
expected_stdout = 'Detected x86_64 architecture'
|
||||
expected_stdout = info_box + ' FTL Checks...'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + ' Detected x86_64 architecture'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + ' Downloading and Installing FTL'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_FTL_detect_unknown_no_errors(Pihole):
|
||||
''' confirms only generic package is downloaded for FTL engine '''
|
||||
# mock uname to return generic platform
|
||||
mock_command('uname', {'-m':('mips', '0')}, Pihole)
|
||||
mock_command('uname', {'-m': ('mips', '0')}, Pihole)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
@@ -356,30 +476,42 @@ def test_FTL_detect_unknown_no_errors(Pihole):
|
||||
expected_stdout = 'Not able to detect architecture (unknown: mips)'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_FTL_download_aarch64_no_errors(Pihole):
|
||||
''' confirms only aarch64 package is downloaded for FTL engine '''
|
||||
'''
|
||||
confirms only aarch64 package is downloaded for FTL engine
|
||||
'''
|
||||
# mock uname to return generic platform
|
||||
download_binary = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLinstall pihole-FTL-aarch64-linux-gnu
|
||||
''')
|
||||
expected_stdout = 'done'
|
||||
expected_stdout = tick_box + ' Downloading and Installing FTL'
|
||||
assert expected_stdout in download_binary.stdout
|
||||
assert 'failed' not in download_binary.stdout
|
||||
assert 'error' not in download_binary.stdout.lower()
|
||||
|
||||
|
||||
def test_FTL_download_unknown_fails_no_errors(Pihole):
|
||||
''' confirms unknown binary is not downloaded for FTL engine '''
|
||||
'''
|
||||
confirms unknown binary is not downloaded for FTL engine
|
||||
'''
|
||||
# mock uname to return generic platform
|
||||
download_binary = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLinstall pihole-FTL-mips
|
||||
''')
|
||||
expected_stdout = 'failed'
|
||||
expected_stdout = cross_box + ' Downloading and Installing FTL'
|
||||
assert expected_stdout in download_binary.stdout
|
||||
assert 'done' not in download_binary.stdout
|
||||
error1 = 'Error: URL https://github.com/pi-hole/FTL/releases/download/'
|
||||
assert error1 in download_binary.stdout
|
||||
error2 = 'not found'
|
||||
assert error2 in download_binary.stdout
|
||||
|
||||
|
||||
def test_FTL_binary_installed_and_responsive_no_errors(Pihole):
|
||||
''' confirms FTL binary is copied and functional in installed location '''
|
||||
'''
|
||||
confirms FTL binary is copied and functional in installed location
|
||||
'''
|
||||
installed_binary = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
@@ -388,8 +520,11 @@ def test_FTL_binary_installed_and_responsive_no_errors(Pihole):
|
||||
expected_stdout = 'v'
|
||||
assert expected_stdout in installed_binary.stdout
|
||||
|
||||
|
||||
# def test_FTL_support_files_installed(Pihole):
|
||||
# ''' confirms FTL support files are installed '''
|
||||
# '''
|
||||
# confirms FTL support files are installed
|
||||
# '''
|
||||
# support_files = Pihole.run('''
|
||||
# source /opt/pihole/basic-install.sh
|
||||
# FTLdetect
|
||||
@@ -402,21 +537,46 @@ def test_FTL_binary_installed_and_responsive_no_errors(Pihole):
|
||||
# assert '644 /run/pihole-FTL.pid' in support_files.stdout
|
||||
# assert '644 /var/log/pihole-FTL.log' in support_files.stdout
|
||||
|
||||
|
||||
def test_IPv6_only_link_local(Pihole):
|
||||
''' confirms IPv6 blocking is disabled for Link-local address '''
|
||||
'''
|
||||
confirms IPv6 blocking is disabled for Link-local address
|
||||
'''
|
||||
# mock ip -6 address to return Link-local address
|
||||
mock_command_2('ip', {'-6 address':('inet6 fe80::d210:52fa:fe00:7ad7/64 scope link', '0')}, Pihole)
|
||||
mock_command_2(
|
||||
'ip',
|
||||
{
|
||||
'-6 address': (
|
||||
'inet6 fe80::d210:52fa:fe00:7ad7/64 scope link',
|
||||
'0'
|
||||
)
|
||||
},
|
||||
Pihole
|
||||
)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
useIPv6dialog
|
||||
''')
|
||||
expected_stdout = 'Found neither IPv6 ULA nor GUA address, blocking IPv6 ads will not be enabled'
|
||||
expected_stdout = ('Unable to find IPv6 ULA/GUA address, '
|
||||
'IPv6 adblocking will not be enabled')
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_IPv6_only_ULA(Pihole):
|
||||
''' confirms IPv6 blocking is enabled for ULA addresses '''
|
||||
'''
|
||||
confirms IPv6 blocking is enabled for ULA addresses
|
||||
'''
|
||||
# mock ip -6 address to return ULA address
|
||||
mock_command_2('ip', {'-6 address':('inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global', '0')}, Pihole)
|
||||
mock_command_2(
|
||||
'ip',
|
||||
{
|
||||
'-6 address': (
|
||||
'inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global',
|
||||
'0'
|
||||
)
|
||||
},
|
||||
Pihole
|
||||
)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
useIPv6dialog
|
||||
@@ -424,10 +584,22 @@ def test_IPv6_only_ULA(Pihole):
|
||||
expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_IPv6_only_GUA(Pihole):
|
||||
''' confirms IPv6 blocking is enabled for GUA addresses '''
|
||||
'''
|
||||
confirms IPv6 blocking is enabled for GUA addresses
|
||||
'''
|
||||
# mock ip -6 address to return GUA address
|
||||
mock_command_2('ip', {'-6 address':('inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global', '0')}, Pihole)
|
||||
mock_command_2(
|
||||
'ip',
|
||||
{
|
||||
'-6 address': (
|
||||
'inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global',
|
||||
'0'
|
||||
)
|
||||
},
|
||||
Pihole
|
||||
)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
useIPv6dialog
|
||||
@@ -435,10 +607,23 @@ def test_IPv6_only_GUA(Pihole):
|
||||
expected_stdout = 'Found IPv6 GUA address, using it for blocking IPv6 ads'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_IPv6_GUA_ULA_test(Pihole):
|
||||
''' confirms IPv6 blocking is enabled for GUA and ULA addresses '''
|
||||
'''
|
||||
confirms IPv6 blocking is enabled for GUA and ULA addresses
|
||||
'''
|
||||
# mock ip -6 address to return GUA and ULA addresses
|
||||
mock_command_2('ip', {'-6 address':('inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global\ninet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global', '0')}, Pihole)
|
||||
mock_command_2(
|
||||
'ip',
|
||||
{
|
||||
'-6 address': (
|
||||
'inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global\n'
|
||||
'inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global',
|
||||
'0'
|
||||
)
|
||||
},
|
||||
Pihole
|
||||
)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
useIPv6dialog
|
||||
@@ -446,61 +631,26 @@ def test_IPv6_GUA_ULA_test(Pihole):
|
||||
expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_IPv6_ULA_GUA_test(Pihole):
|
||||
''' confirms IPv6 blocking is enabled for GUA and ULA addresses '''
|
||||
'''
|
||||
confirms IPv6 blocking is enabled for GUA and ULA addresses
|
||||
'''
|
||||
# mock ip -6 address to return ULA and GUA addresses
|
||||
mock_command_2('ip', {'-6 address':('inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global\ninet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global', '0')}, Pihole)
|
||||
mock_command_2(
|
||||
'ip',
|
||||
{
|
||||
'-6 address': (
|
||||
'inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global\n'
|
||||
'inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global',
|
||||
'0'
|
||||
)
|
||||
},
|
||||
Pihole
|
||||
)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
useIPv6dialog
|
||||
''')
|
||||
expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads'
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
# Helper functions
|
||||
def mock_command(script, args, container):
|
||||
''' Allows for setup of commands we don't really want to have to run for real in unit tests '''
|
||||
full_script_path = '/usr/local/bin/{}'.format(script)
|
||||
mock_script = dedent('''\
|
||||
#!/bin/bash -e
|
||||
echo "\$0 \$@" >> /var/log/{script}
|
||||
case "\$1" in'''.format(script=script))
|
||||
for k, v in args.iteritems():
|
||||
case = dedent('''
|
||||
{arg})
|
||||
echo {res}
|
||||
exit {retcode}
|
||||
;;'''.format(arg=k, res=v[0], retcode=v[1]))
|
||||
mock_script += case
|
||||
mock_script += dedent('''
|
||||
esac''')
|
||||
container.run('''
|
||||
cat <<EOF> {script}\n{content}\nEOF
|
||||
chmod +x {script}
|
||||
rm -f /var/log/{scriptlog}'''.format(script=full_script_path, content=mock_script, scriptlog=script))
|
||||
|
||||
def mock_command_2(script, args, container):
|
||||
''' Allows for setup of commands we don't really want to have to run for real in unit tests '''
|
||||
full_script_path = '/usr/local/bin/{}'.format(script)
|
||||
mock_script = dedent('''\
|
||||
#!/bin/bash -e
|
||||
echo "\$0 \$@" >> /var/log/{script}
|
||||
case "\$1 \$2" in'''.format(script=script))
|
||||
for k, v in args.iteritems():
|
||||
case = dedent('''
|
||||
\"{arg}\")
|
||||
echo \"{res}\"
|
||||
exit {retcode}
|
||||
;;'''.format(arg=k, res=v[0], retcode=v[1]))
|
||||
mock_script += case
|
||||
mock_script += dedent('''
|
||||
esac''')
|
||||
container.run('''
|
||||
cat <<EOF> {script}\n{content}\nEOF
|
||||
chmod +x {script}
|
||||
rm -f /var/log/{scriptlog}'''.format(script=full_script_path, content=mock_script, scriptlog=script))
|
||||
|
||||
def run_script(Pihole, script):
|
||||
result = Pihole.run(script)
|
||||
assert result.rc == 0
|
||||
return result
|
||||
|
202
test/test_centos_fedora_support.py
Normal file
202
test/test_centos_fedora_support.py
Normal file
@@ -0,0 +1,202 @@
|
||||
import pytest
|
||||
from conftest import (
|
||||
tick_box,
|
||||
info_box,
|
||||
cross_box,
|
||||
mock_command,
|
||||
mock_command_2,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", [('fedora'), ])
|
||||
def test_epel_and_remi_not_installed_fedora(Pihole):
|
||||
'''
|
||||
confirms installer does not attempt to install EPEL/REMI repositories
|
||||
on Fedora
|
||||
'''
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
''')
|
||||
assert distro_check.stdout == ''
|
||||
|
||||
epel_package = Pihole.package('epel-release')
|
||||
assert not epel_package.is_installed
|
||||
remi_package = Pihole.package('remi-release')
|
||||
assert not remi_package.is_installed
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", [('centos'), ])
|
||||
def test_release_supported_version_check_centos(Pihole):
|
||||
'''
|
||||
confirms installer exits on unsupported releases of CentOS
|
||||
'''
|
||||
# modify /etc/redhat-release to mock an unsupported CentOS release
|
||||
Pihole.run('echo "CentOS Linux release 6.9" > /etc/redhat-release')
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
''')
|
||||
expected_stdout = cross_box + (' CentOS 6 is not supported.')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
expected_stdout = 'Please update to CentOS release 7 or later'
|
||||
assert expected_stdout in distro_check.stdout
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", [('centos'), ])
|
||||
def test_enable_epel_repository_centos(Pihole):
|
||||
'''
|
||||
confirms the EPEL package repository is enabled when installed on CentOS
|
||||
'''
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
''')
|
||||
expected_stdout = info_box + (' Enabling EPEL package repository '
|
||||
'(https://fedoraproject.org/wiki/EPEL)')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
expected_stdout = tick_box + ' Installed epel-release'
|
||||
assert expected_stdout in distro_check.stdout
|
||||
epel_package = Pihole.package('epel-release')
|
||||
assert epel_package.is_installed
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", [('centos'), ])
|
||||
def test_php_upgrade_default_optout_centos(Pihole):
|
||||
'''
|
||||
confirms the default behavior to opt-out of installing PHP7 from REMI
|
||||
'''
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
''')
|
||||
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
|
||||
'Deprecated PHP may be in use.')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
remi_package = Pihole.package('remi-release')
|
||||
assert not remi_package.is_installed
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", [('centos'), ])
|
||||
def test_php_upgrade_user_optout_centos(Pihole):
|
||||
'''
|
||||
confirms installer behavior when user opt-out of installing PHP7 from REMI
|
||||
(php not currently installed)
|
||||
'''
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*': ('', '1')}, Pihole)
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
''')
|
||||
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
|
||||
'Deprecated PHP may be in use.')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
remi_package = Pihole.package('remi-release')
|
||||
assert not remi_package.is_installed
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", [('centos'), ])
|
||||
def test_php_upgrade_user_optin_centos(Pihole):
|
||||
'''
|
||||
confirms installer behavior when user opt-in to installing PHP7 from REMI
|
||||
(php not currently installed)
|
||||
'''
|
||||
# Whiptail dialog returns Continue for user prompt
|
||||
mock_command('whiptail', {'*': ('', '0')}, Pihole)
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
''')
|
||||
assert 'opt-out' not in distro_check.stdout
|
||||
expected_stdout = info_box + (' Enabling Remi\'s RPM repository '
|
||||
'(https://rpms.remirepo.net)')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
expected_stdout = tick_box + (' Remi\'s RPM repository has '
|
||||
'been enabled for PHP7')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
remi_package = Pihole.package('remi-release')
|
||||
assert remi_package.is_installed
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", [('centos'), ])
|
||||
def test_php_version_lt_7_detected_upgrade_default_optout_centos(Pihole):
|
||||
'''
|
||||
confirms the default behavior to opt-out of upgrading to PHP7 from REMI
|
||||
'''
|
||||
# first we will install the default php version to test installer behavior
|
||||
php_install = Pihole.run('yum install -y php')
|
||||
assert php_install.rc == 0
|
||||
php_package = Pihole.package('php')
|
||||
default_centos_php_version = php_package.version.split('.')[0]
|
||||
if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended
|
||||
pytest.skip("Test deprecated . Detected default PHP version >= 7")
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
''')
|
||||
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
|
||||
'Deprecated PHP may be in use.')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
remi_package = Pihole.package('remi-release')
|
||||
assert not remi_package.is_installed
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", [('centos'), ])
|
||||
def test_php_version_lt_7_detected_upgrade_user_optout_centos(Pihole):
|
||||
'''
|
||||
confirms installer behavior when user opt-out to upgrade to PHP7 via REMI
|
||||
'''
|
||||
# first we will install the default php version to test installer behavior
|
||||
php_install = Pihole.run('yum install -y php')
|
||||
assert php_install.rc == 0
|
||||
php_package = Pihole.package('php')
|
||||
default_centos_php_version = php_package.version.split('.')[0]
|
||||
if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended
|
||||
pytest.skip("Test deprecated . Detected default PHP version >= 7")
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*': ('', '1')}, Pihole)
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
''')
|
||||
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
|
||||
'Deprecated PHP may be in use.')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
remi_package = Pihole.package('remi-release')
|
||||
assert not remi_package.is_installed
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", [('centos'), ])
|
||||
def test_php_version_lt_7_detected_upgrade_user_optin_centos(Pihole):
|
||||
'''
|
||||
confirms installer behavior when user opt-in to upgrade to PHP7 via REMI
|
||||
'''
|
||||
# first we will install the default php version to test installer behavior
|
||||
php_install = Pihole.run('yum install -y php')
|
||||
assert php_install.rc == 0
|
||||
php_package = Pihole.package('php')
|
||||
default_centos_php_version = php_package.version.split('.')[0]
|
||||
if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended
|
||||
pytest.skip("Test deprecated . Detected default PHP version >= 7")
|
||||
# Whiptail dialog returns Continue for user prompt
|
||||
mock_command('whiptail', {'*': ('', '0')}, Pihole)
|
||||
distro_check = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
install_dependent_packages PIHOLE_WEB_DEPS[@]
|
||||
''')
|
||||
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
|
||||
'Deprecated PHP may be in use.')
|
||||
assert expected_stdout not in distro_check.stdout
|
||||
expected_stdout = info_box + (' Enabling Remi\'s RPM repository '
|
||||
'(https://rpms.remirepo.net)')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
expected_stdout = tick_box + (' Remi\'s RPM repository has '
|
||||
'been enabled for PHP7')
|
||||
assert expected_stdout in distro_check.stdout
|
||||
remi_package = Pihole.package('remi-release')
|
||||
assert remi_package.is_installed
|
||||
updated_php_package = Pihole.package('php')
|
||||
updated_php_version = updated_php_package.version.split('.')[0]
|
||||
assert int(updated_php_version) == 7
|
@@ -1,13 +1,18 @@
|
||||
import pytest
|
||||
import testinfra
|
||||
|
||||
run_local = testinfra.get_backend(
|
||||
"local://"
|
||||
).get_module("Command").run
|
||||
|
||||
|
||||
def test_scripts_pass_shellcheck():
|
||||
''' Make sure shellcheck does not find anything wrong with our shell scripts '''
|
||||
shellcheck = "find . -type f \( -name 'update.sh' -o -name 'piholeDebug.sh' \) | while read file; do shellcheck \"$file\"; done;"
|
||||
'''
|
||||
Make sure shellcheck does not find anything wrong with our shell scripts
|
||||
'''
|
||||
shellcheck = ("find . -type f -name 'update.sh' "
|
||||
"| while read file; do "
|
||||
"shellcheck -x \"$file\" -e SC1090,SC1091; "
|
||||
"done;")
|
||||
results = run_local(shellcheck)
|
||||
print results.stdout
|
||||
assert '' == results.stdout
|
||||
|
10
tox.ini
Normal file
10
tox.ini
Normal file
@@ -0,0 +1,10 @@
|
||||
[tox]
|
||||
envlist = py27
|
||||
|
||||
[testenv]
|
||||
whitelist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
commands = docker build -f test/debian.Dockerfile -t pytest_pihole:debian .
|
||||
docker build -f test/centos.Dockerfile -t pytest_pihole:centos .
|
||||
docker build -f test/fedora.Dockerfile -t pytest_pihole:fedora .
|
||||
pytest {posargs:-vv -n auto} -m "not build_stage" ./test/
|
Reference in New Issue
Block a user