Compare commits
1816 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1e87850952 | ||
|
9f2ce0e296 | ||
|
55e10d8287 | ||
|
94c772e1a8 | ||
|
2b6d9c34c9 | ||
|
535b8458d4 | ||
|
49b3f82675 | ||
|
9543203610 | ||
|
cacfe4d387 | ||
|
a7f0137e5f | ||
|
b2eb364a4b | ||
|
bb3b1bc6ba | ||
|
ae8eb3f177 | ||
|
27c7325174 | ||
|
8ee3d810b0 | ||
|
d7dde06552 | ||
|
448495a4a3 | ||
|
52cc8e2fcf | ||
|
fb9cdea008 | ||
|
6c54bf036c | ||
|
657bc4edcd | ||
|
b75dd5ff73 | ||
|
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 | ||
|
9631519eb1 | ||
|
557d076d2e | ||
|
8a40c91d1a | ||
|
90efa3b610 | ||
|
ec4e4d3b72 | ||
|
63312ac4b8 | ||
|
d158a7d51e | ||
|
209a2ab3ec | ||
|
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 | ||
|
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 | ||
|
ecde222512 | ||
|
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 | ||
|
26fcb1b2a0 | ||
|
e9c4e8123c | ||
|
a290e01bdf | ||
|
9cc392fa02 | ||
|
7c2046cce7 | ||
|
4d731ca30b | ||
|
f2016f26d7 | ||
|
b13171cc45 | ||
|
9c645e2010 | ||
|
bb6f409e89 | ||
|
61ec7723f6 | ||
|
9c136a5579 | ||
|
a5733508ae | ||
|
a8818c16d8 | ||
|
c8e1e6dc8a | ||
|
7bcea98d0c | ||
|
74b15d10d9 | ||
|
356c70cdae | ||
|
dfb5c37d98 | ||
|
30dcf6ff47 | ||
|
a0603ad3b7 | ||
|
551add5f44 | ||
|
88944a31ec | ||
|
3473ebc0fe | ||
|
8c657910ae | ||
|
1886c44579 | ||
|
1dcc1871fb | ||
|
19e688effb | ||
|
6a2b885988 | ||
|
bfc4775b34 | ||
|
981004606b | ||
|
9d40fd1eda | ||
|
db1e5f10ea | ||
|
fbb6b56d99 | ||
|
f1f70133dc | ||
|
d0f55e5125 | ||
|
5e308dbd51 | ||
|
c3c554f4d6 | ||
|
320c06e0a4 | ||
|
2e398c5da4 | ||
|
b1484104bd | ||
|
c738a9a044 | ||
|
519cb71022 | ||
|
fcd92d27f7 | ||
|
3eacfa9831 | ||
|
ff6df76e36 | ||
|
1d64b614c7 | ||
|
a832dd7f67 | ||
|
a96b4d28e1 | ||
|
57e8b7f924 | ||
|
263eb856d5 | ||
|
5a4f088b24 | ||
|
c1913bfa7d | ||
|
474881e4c7 | ||
|
7e0aa822b6 | ||
|
940d780a4c | ||
|
ad9575ce18 | ||
|
ce86205df0 | ||
|
3cf9942465 | ||
|
153031482f | ||
|
1f74b1e2fd | ||
|
42393123a0 | ||
|
af2cff5177 | ||
|
5435b93df2 | ||
|
3a3dde6298 | ||
|
b4bc90fb85 | ||
|
02040cd25d | ||
|
bdc6bd4135 | ||
|
e720de401d | ||
|
ce97896ffd | ||
|
86fa4e9ee8 | ||
|
f09c166350 | ||
|
24868fdb2b | ||
|
a463250ecf | ||
|
d38e034e92 | ||
|
6eb9192cd1 | ||
|
94f15f1b3c | ||
|
ee296f36c1 | ||
|
598a7b3cc0 | ||
|
52baf26d7d | ||
|
fb3e6ee35c | ||
|
af1d1bd9c2 | ||
|
f32ad7699d | ||
|
79294bb6ca | ||
|
1cd4710718 | ||
|
48c19ade8e | ||
|
0acf70f836 | ||
|
fd584dd03b | ||
|
2740c68a63 | ||
|
62a242a894 | ||
|
8b060e9699 | ||
|
b1f769b671 | ||
|
77378da70a | ||
|
160030b75f | ||
|
0a0c3a2fb7 | ||
|
e9f1ca338f | ||
|
073c7e54df | ||
|
2834f2ccc2 | ||
|
6c56665403 | ||
|
0d794226ab | ||
|
ab2af11775 | ||
|
83662c9e50 | ||
|
6c1d52199f | ||
|
cfaf1ac67c | ||
|
a6caa0e680 | ||
|
c4b43f92ce | ||
|
3d1ccd9625 | ||
|
76bd53ef1f | ||
|
8fa209f0df | ||
|
88835512dd | ||
|
6b862dd9e9 | ||
|
be0b76c954 | ||
|
e45559da20 | ||
|
d7b5870ba6 | ||
|
bb7a7d94ef | ||
|
9a475cc010 | ||
|
ae30c285a2 | ||
|
df18c7cd59 | ||
|
2e74219ff9 | ||
|
b0ae954f1e | ||
|
a2404f104a | ||
|
38547ced7a | ||
|
9a9d9007cd | ||
|
bd2d81f691 | ||
|
1b9e7fbf2e | ||
|
d4a49d192f | ||
|
8cb66544d2 | ||
|
140ac192aa | ||
|
b961b13d60 | ||
|
febdb4a190 | ||
|
1d60b62e7a | ||
|
41e1e4cb68 | ||
|
d5b88e0df8 | ||
|
20fd61468d | ||
|
0a0d25dff4 | ||
|
38ba079baa | ||
|
33f7979359 | ||
|
8460b2544b | ||
|
bc514ea955 | ||
|
6f8893d950 | ||
|
de6aaf18ab | ||
|
0fe64cf5cc | ||
|
9a95531fb9 | ||
|
9df6a8dd06 | ||
|
1590a179fa | ||
|
5e16487ef6 | ||
|
2b3afbfef8 | ||
|
c3c7dcc9f5 | ||
|
273728b481 | ||
|
81a1057cac | ||
|
fd310c6445 | ||
|
a9b52518bf | ||
|
87da40068c | ||
|
b8f1eadb7f | ||
|
b522d8eaf6 | ||
|
89ff99322d | ||
|
56e17d1010 | ||
|
708067e875 | ||
|
d2ab0694b7 | ||
|
8a14a63d5d | ||
|
8235b18854 | ||
|
e0e9ebbe74 | ||
|
6e6259975e | ||
|
10bc8414b9 | ||
|
4e25e0dc5c | ||
|
b1a9793d94 | ||
|
6dea00668e | ||
|
ae9182c92e | ||
|
af17355fe7 | ||
|
dce3e50a00 | ||
|
cf31561267 | ||
|
a97f0b1298 | ||
|
d1e0f3ae18 | ||
|
47a6786e8f | ||
|
a69fcbb91e | ||
|
8e2b51b391 | ||
|
560af43204 | ||
|
9c119f919e | ||
|
baefec86f2 | ||
|
eb763d2dc2 | ||
|
512c650441 | ||
|
dc44fc9e27 | ||
|
05640f9a6b | ||
|
6f2fb57c08 | ||
|
2547cc4c8d | ||
|
112ddb3c77 | ||
|
9f4ef66f41 | ||
|
086f0790fc | ||
|
709b44f736 | ||
|
6cd4ff6d68 | ||
|
abd3e828de | ||
|
b85af50d14 | ||
|
0e8fd49669 | ||
|
945e22874e | ||
|
77ab47a984 | ||
|
ed8088f203 | ||
|
8831b22fc8 | ||
|
0341bd1758 | ||
|
9bb4a5fb25 | ||
|
ebfffea5dc | ||
|
81939ab265 | ||
|
f2fe84c9d3 | ||
|
051f463350 | ||
|
f626406685 | ||
|
dd971b6ee5 | ||
|
8776b822db | ||
|
fc76b1a6a3 | ||
|
9183200b6f | ||
|
f1b8abf503 | ||
|
9502356980 | ||
|
a535ca9db4 | ||
|
2c762899de | ||
|
24fd23493d | ||
|
6f1ed28d0a | ||
|
66b7d04b82 | ||
|
044afa838c | ||
|
7ba47f504c | ||
|
0be1717ff4 | ||
|
189a4e0078 | ||
|
3adf8785d8 | ||
|
b74862bfc5 | ||
|
01273124ea | ||
|
721ada7e16 | ||
|
bd9dc91396 | ||
|
de6c43a8d3 | ||
|
93dea7b942 | ||
|
f6fc6a5e56 | ||
|
ca24f7c143 | ||
|
17b0db6515 | ||
|
83b0600863 | ||
|
38961fb31b | ||
|
6c130b7960 | ||
|
7244d44a1d | ||
|
9b060aab34 | ||
|
ba5bbf3523 | ||
|
697b0295f3 | ||
|
66d7ebd6c3 | ||
|
ae24f1255f | ||
|
ec7e75a6e3 | ||
|
aee106ae69 | ||
|
2a881a90ac | ||
|
ce6c465942 | ||
|
3748a0ed33 | ||
|
7a1a2dec67 | ||
|
7ed1bbad49 | ||
|
078cc7660e | ||
|
af2893d2ce | ||
|
0fe5efba76 | ||
|
fb6631d317 | ||
|
cd0b8927c5 | ||
|
3fab34687c | ||
|
b2d78edae9 | ||
|
412cacac49 | ||
|
dcb9797f35 | ||
|
4dcee5cd84 | ||
|
a64211123f | ||
|
1645677c3a | ||
|
4f85ace525 | ||
|
e8fde702a0 | ||
|
e339f3852c | ||
|
032f94afc0 | ||
|
e9b50442fa | ||
|
77b3764481 | ||
|
90ccbef431 | ||
|
02ea9b9abc | ||
|
4cd598ae10 | ||
|
8eeb8ad779 | ||
|
2ffb103acb | ||
|
a0c17368ed | ||
|
d76e761d0b | ||
|
6023984703 | ||
|
cde7b53de3 | ||
|
b36a44a954 | ||
|
fcc3d5450d | ||
|
057cecb3e0 | ||
|
86c6e0e826 | ||
|
5c223b5f4e | ||
|
883aa30aca | ||
|
763b51fe22 | ||
|
dd1aa9163c | ||
|
e087797edc | ||
|
5e6f8489a9 | ||
|
48351fed79 | ||
|
875a5d309d | ||
|
70e876ee13 | ||
|
7a269e757e | ||
|
87edbeaf58 | ||
|
339f95b00c | ||
|
e480c761cd | ||
|
26c628f8a5 | ||
|
59d6907d71 | ||
|
b4450a3918 | ||
|
7032be6049 | ||
|
70a6a79b8c | ||
|
f24f77c5bd | ||
|
66ae79f9b8 | ||
|
378338c684 | ||
|
7da076df18 | ||
|
45fbadbb26 | ||
|
a7def771c8 | ||
|
543f1243e2 | ||
|
a001443ad7 | ||
|
36166c129a | ||
|
4e7a485e23 | ||
|
9e5795bf55 | ||
|
053b38e0bd | ||
|
2aa3a109a0 | ||
|
472708376d | ||
|
cfed3d59e9 | ||
|
95fd9d4863 | ||
|
c89bca6ec8 | ||
|
ee262c30c2 | ||
|
6b8240d4de | ||
|
4035c933df | ||
|
abd44dd284 | ||
|
95d7fe76b5 | ||
|
a052f397fe | ||
|
09d6e73b0a | ||
|
7fb6b71d52 | ||
|
07e37d7fc3 | ||
|
fbe3dc0dcd | ||
|
6018c0c2fc | ||
|
52a2f166fd | ||
|
26cb6a1929 | ||
|
7c63bbfe44 | ||
|
109026222e | ||
|
af8e629df4 | ||
|
f7f1daa69f | ||
|
01f980d49c | ||
|
99fab7e52a | ||
|
692292b1d4 | ||
|
cd608d9d5b | ||
|
ba67144e34 | ||
|
162d9d7d57 | ||
|
357f5a2cfd | ||
|
d1ca32b0a5 | ||
|
34f326c559 | ||
|
7785dac50e | ||
|
01f643e5eb | ||
|
8037f3e332 | ||
|
19e30b829a | ||
|
afe5176e01 | ||
|
a48317883d | ||
|
5ac92ae4eb | ||
|
8fb6ba19a1 | ||
|
314f7e7889 | ||
|
4fcf8fd23f | ||
|
10a30344e5 | ||
|
1206dda347 | ||
|
b764d17c64 | ||
|
ba0abdb88d | ||
|
1428b58dde | ||
|
e57425df5f | ||
|
5e7dfaf220 | ||
|
ad5c011b6c | ||
|
fcdd33b585 | ||
|
f767f066ad | ||
|
b8d0e5e5a1 | ||
|
26ad23f01e | ||
|
21d771e171 | ||
|
66616eb0f0 | ||
|
18eb8a2159 | ||
|
72a1fc3f64 | ||
|
96eea32a9d | ||
|
3239c7023a | ||
|
5333895a9f | ||
|
da05491992 | ||
|
b9f5d79f3d | ||
|
fddc515ee1 | ||
|
3d4516dc95 | ||
|
509c864cc3 | ||
|
8b22f435ad | ||
|
55d39595e1 | ||
|
f8944177a0 | ||
|
3060c4d887 | ||
|
13ee5dc728 | ||
|
06873fe69e | ||
|
a8ac212ee6 | ||
|
3d9d13222b | ||
|
745adabb05 | ||
|
3861b57dc6 | ||
|
99a4a80017 | ||
|
033ba26041 | ||
|
439999cb62 | ||
|
7291aa07ca | ||
|
9181e2652e | ||
|
409f76aa34 | ||
|
54e2c6181a | ||
|
a281d87315 | ||
|
2a5587f236 | ||
|
2d18b2d784 | ||
|
585f842206 | ||
|
8cb4304c13 | ||
|
737819b56e | ||
|
a7130e6530 | ||
|
f9cfed5aff | ||
|
f65a9dbfd2 | ||
|
96c59fada4 | ||
|
153a9d8ac7 | ||
|
297d4eec57 | ||
|
c31b4383e6 | ||
|
0375a3caa3 | ||
|
08cddba200 | ||
|
1bf43b0425 | ||
|
35828f9cea | ||
|
90af12fdb8 | ||
|
dc63182647 | ||
|
5fede23cf7 | ||
|
bc4762f270 | ||
|
01429d59bd | ||
|
731d15f9b5 | ||
|
f0bd7fae5c | ||
|
f8322cc2d4 | ||
|
dfdb9e393b | ||
|
bd07d7f32e | ||
|
f588c6f93c | ||
|
d9ec3d2c22 | ||
|
e2b87759d8 | ||
|
52e0aa11af | ||
|
1421c31179 | ||
|
d5587e32d0 | ||
|
28eb348707 | ||
|
91bcc18e6a | ||
|
5b43f13935 | ||
|
85b3fef08d | ||
|
cc7c48237c | ||
|
15037fa888 | ||
|
d595fef18f | ||
|
be5fa22b6f | ||
|
a5c6bbeee7 | ||
|
094a645e60 | ||
|
51acdfa633 | ||
|
2c16a75ef1 | ||
|
6fd7e0311c | ||
|
1bcf2dd0fc | ||
|
9a3cf949cf | ||
|
cec214f900 | ||
|
dad669d68b | ||
|
561f40d97e | ||
|
dad18dc5de | ||
|
5c95c4074b | ||
|
4301b9a12a | ||
|
0bbe0aed83 | ||
|
b16f797317 | ||
|
4bb71ae046 | ||
|
679b098aa7 | ||
|
e0e88fdb52 | ||
|
8bba3a257c | ||
|
8529c1287f | ||
|
9c7f7756b4 | ||
|
f1cfb16bf9 | ||
|
95796e1978 | ||
|
968b981ecb | ||
|
3aeb378b56 | ||
|
28bafe7427 | ||
|
1317b67657 | ||
|
3f462c771f | ||
|
31aa42c35e | ||
|
f7a17248b7 | ||
|
b60e6310bf | ||
|
6a89c6bf3b | ||
|
b3b7aae7d7 | ||
|
fe8c365d17 | ||
|
9acc3aac01 | ||
|
1ad23a065e | ||
|
de102fde5c | ||
|
77554fbd13 | ||
|
6863bff7c5 | ||
|
eaf6938c35 | ||
|
34ad2157dd | ||
|
0635309f23 | ||
|
2661ef53a2 | ||
|
eddca8f127 | ||
|
cef0211c00 | ||
|
86052540d9 | ||
|
930cb15e2c | ||
|
8bb9dd460b | ||
|
8a3c78ca0a | ||
|
62a5e36afd | ||
|
00b28f0aed | ||
|
7c94aa9f07 | ||
|
ec8c40b69b | ||
|
2981f3cbd1 | ||
|
c2e1819098 | ||
|
58f3ff69d8 | ||
|
72d8d10e64 | ||
|
63d02df0bc | ||
|
f579fd3895 | ||
|
34df34ba27 | ||
|
2689b37c35 | ||
|
9b6427144f | ||
|
9212eea8bd | ||
|
6de5d6dd0a | ||
|
08f08fea61 | ||
|
1ed2a8637f | ||
|
d8bcbdadd6 | ||
|
d196c13f2c | ||
|
7ba251d3a6 | ||
|
0b72c639fb | ||
|
bd1c5a42e8 | ||
|
845d8c0e63 | ||
|
bcb8a52418 | ||
|
322cb2387b | ||
|
bfe56942f9 | ||
|
b4c32e47c6 | ||
|
248d8680f7 | ||
|
1bda965a7c | ||
|
2ee305769d | ||
|
3a8a936575 | ||
|
6b6fbc4709 | ||
|
3fd2ffd466 | ||
|
b56ca2b834 | ||
|
10f77df8bb | ||
|
df7671d393 | ||
|
a263936243 | ||
|
11924d425b | ||
|
0bd5a5f382 | ||
|
757eb64be3 | ||
|
6b3aea933d | ||
|
2935275227 | ||
|
cbe045b946 | ||
|
c58a95ca2e | ||
|
80a3bce6d5 | ||
|
6f0289de49 | ||
|
0966d7660e | ||
|
27e90cc4e6 | ||
|
b785213c3a | ||
|
642a73508d | ||
|
8f7b023769 | ||
|
a122fb2900 | ||
|
a299a2cc5f | ||
|
47196d86ad | ||
|
a713cf7952 | ||
|
6cd7b8ff5e | ||
|
f8264f8277 | ||
|
0e4d5e9103 | ||
|
f599bcfef9 | ||
|
74a4e62cc9 | ||
|
63a414a544 | ||
|
7d1f5091a7 | ||
|
3b54cab3bc | ||
|
52d06d906e | ||
|
d9e949b27c | ||
|
b65fe9d64f | ||
|
168397e90d | ||
|
e3c1fcd2c6 | ||
|
a2bc86fbcb | ||
|
26c6446252 | ||
|
e5e44db5ac | ||
|
d4f833c739 | ||
|
806f44abe6 | ||
|
900bdc5ee2 | ||
|
200995bf29 | ||
|
3a90c1c192 | ||
|
cc68155dfa | ||
|
b8545eb1df | ||
|
76531da340 | ||
|
12bec1df68 | ||
|
2b778695b1 | ||
|
ad61852804 | ||
|
dbd8aee4ee | ||
|
677694b01a | ||
|
85f0241c0d | ||
|
ade2185a9f | ||
|
0d27005dda | ||
|
8ee2bdec4d | ||
|
de6ce276d0 | ||
|
fbea81dcd7 | ||
|
502c349b8b | ||
|
5fb0aa70de | ||
|
7750e1344c | ||
|
8be37130e9 | ||
|
fa055481a7 | ||
|
d080e5d7a8 | ||
|
ad07655630 | ||
|
7cceb8615a | ||
|
ab9c8f4859 | ||
|
ffb8a74111 | ||
|
45587194e5 | ||
|
ccbf391913 | ||
|
ebf0db4bbf | ||
|
7765efa6c4 | ||
|
02d4b6794c | ||
|
836b717346 | ||
|
9ac265980f | ||
|
40798da6b1 | ||
|
fc596e41d4 | ||
|
1f9b0f7cef | ||
|
7bcc15e416 | ||
|
1a3bdbaabf | ||
|
5e35fdbc52 | ||
|
ab2c486f25 | ||
|
7fd7430d38 | ||
|
089b98430f | ||
|
5c7fc05a32 | ||
|
ced0d3c2c0 | ||
|
1afc5d351d | ||
|
09bbc81470 | ||
|
f7274addcd | ||
|
09bfa2ef77 | ||
|
a48518d234 | ||
|
a4a9879643 | ||
|
d1ccd7a460 | ||
|
9181a4a1d8 | ||
|
3268e1611a | ||
|
ea9ec384c6 | ||
|
bbb958b7ed | ||
|
cf724176dc | ||
|
18d1c98f08 | ||
|
d0cd39a25f | ||
|
03d4fcd17d | ||
|
02d658be65 | ||
|
1e627c7e8f | ||
|
9170488b0a | ||
|
b02730a5ad | ||
|
9af26cbaac | ||
|
73741f1518 | ||
|
9a9cb61345 | ||
|
6abd6d8879 | ||
|
c3b51b4ceb | ||
|
321ea8a3a9 | ||
|
4d6263872d | ||
|
fcdd58ac94 | ||
|
ef8292d371 | ||
|
bc6a985f7c | ||
|
7320fc11d2 | ||
|
51f6d75db4 | ||
|
a328326e39 | ||
|
4eedf8a746 | ||
|
c5f5252145 | ||
|
3f189ae7fe | ||
|
7fadd469c9 | ||
|
823e874d20 | ||
|
739aaafa9a | ||
|
62d001225a | ||
|
e50947eb58 | ||
|
ca056d32d2 | ||
|
63a455f4f7 | ||
|
a0e0465036 | ||
|
d174a9d015 | ||
|
7eb6124721 | ||
|
f458780ba7 | ||
|
8ad52806de | ||
|
dc22a50dcc | ||
|
852341c601 | ||
|
d5ed6c1901 | ||
|
e15548cbf5 | ||
|
5e28e6b9ac | ||
|
c78d43f640 | ||
|
da41383476 | ||
|
6ff79835da | ||
|
1d608b204a | ||
|
c2b8bed3a8 | ||
|
3365ef7aaa | ||
|
68c17b26dc | ||
|
e647efd471 | ||
|
15db1ffdd5 | ||
|
4632b0f797 | ||
|
65c35a5530 | ||
|
c449a1c0e0 | ||
|
b020010f0d | ||
|
0276c72fe2 | ||
|
e4aec05d0f | ||
|
2919f852ad | ||
|
a6e3b9de37 | ||
|
04a9791be2 | ||
|
6b896a1c54 | ||
|
cb8df06685 | ||
|
bfe5506cc1 | ||
|
b64066fec7 | ||
|
ffd31d8330 | ||
|
d89254fedf | ||
|
a771ddf667 | ||
|
ce2e410468 | ||
|
c1982c04ff | ||
|
9a62026830 | ||
|
d12efccd0b | ||
|
54afffed19 | ||
|
fc8fcdbece | ||
|
abd1fedc9d | ||
|
9725985037 | ||
|
754f3359ec | ||
|
4c131b8c28 | ||
|
15c674ba29 | ||
|
00aff6a906 | ||
|
c45c3a72b5 | ||
|
662d450651 | ||
|
a8897becd2 | ||
|
d0126f4454 | ||
|
fdb64a5702 | ||
|
73a80ff7dc | ||
|
a795fd698d | ||
|
2fb0dc0a4a | ||
|
5b4653cf39 | ||
|
aa8e1497a3 | ||
|
eb13d846ef | ||
|
c674a175ee | ||
|
afabf30ec6 | ||
|
420158494d | ||
|
6b7b0e0eb3 | ||
|
ad70db7e0e | ||
|
ef1ce66793 | ||
|
c364fd80b6 | ||
|
3bc5d1bae0 | ||
|
e4e34acba1 | ||
|
ff3c36a7a2 | ||
|
1e7e3259b5 | ||
|
5e53f484be | ||
|
513bc32d87 | ||
|
e7c944ff0b | ||
|
9355a8ad0e | ||
|
a74e48a138 | ||
|
ea5ee7b0f9 | ||
|
bc8cf1b2d8 | ||
|
bb28d94884 | ||
|
a4f58b0a22 | ||
|
bcf8139708 | ||
|
9b0390c9da | ||
|
e88f58c34e | ||
|
8f402f5c77 | ||
|
60054da582 | ||
|
4626b8ced5 | ||
|
ab7d193f98 | ||
|
cbf84c1840 | ||
|
420fb69166 | ||
|
c9c28cb59a | ||
|
9073f34b30 | ||
|
fb5578c0d4 | ||
|
4244f716e0 | ||
|
07a4f970d4 | ||
|
2335097c99 | ||
|
bef0a2fef0 | ||
|
c48e6c91f5 | ||
|
f082b5ba54 | ||
|
8841bdd252 | ||
|
58261098fb | ||
|
9432d3035a | ||
|
9907fc2770 | ||
|
d42caa8672 | ||
|
8117ec8e20 | ||
|
ff2783f9fc | ||
|
156a51c945 | ||
|
c72ffae4a2 | ||
|
7f3b0030ea | ||
|
9a626948f8 | ||
|
5c43df66a8 | ||
|
d0e3c546f8 | ||
|
a9cb93d801 | ||
|
ee8f29d178 | ||
|
da363070c7 | ||
|
50cf891e01 | ||
|
715838cf89 | ||
|
cd0c3f9418 | ||
|
efaee2b68b | ||
|
372c699cc6 | ||
|
8cb01cdd29 | ||
|
7a243f890e | ||
|
10982a0f45 | ||
|
d4c378ed5d | ||
|
7872f68a45 | ||
|
ec8c848106 | ||
|
3b50ce8c54 | ||
|
afabb5957b | ||
|
1eeaa01234 | ||
|
e2898217d2 | ||
|
f81dadc5d0 | ||
|
a0019d86c5 | ||
|
3c4a9bba78 | ||
|
4348653431 | ||
|
09cfa9bb20 | ||
|
e7713a9028 | ||
|
906a1753be | ||
|
a3d519b671 | ||
|
8591f9d576 | ||
|
c10ec5548f | ||
|
684ac98c8e | ||
|
db89fa9881 | ||
|
9a80421d73 | ||
|
2ddacf40c0 | ||
|
92f4824884 | ||
|
9a2ffabc33 | ||
|
2977168da1 | ||
|
85e3f37503 | ||
|
ba015c1918 | ||
|
1556adb678 | ||
|
6bfe729112 | ||
|
c340668870 | ||
|
207422f83a | ||
|
004ba28378 | ||
|
6f38801ed8 | ||
|
1509eb7d82 | ||
|
b8d2bfc890 | ||
|
8a2ee95e4a | ||
|
bc91716082 | ||
|
a00034a6a7 | ||
|
8dc0dc4d69 | ||
|
2f4b7ce3dd | ||
|
d4f83cb1d4 | ||
|
aaab3306a8 | ||
|
2bafa2f2ac | ||
|
9ac378ae09 | ||
|
bf867bd9fd | ||
|
7fd9ff43af | ||
|
bebb569c43 | ||
|
74e33bb1a0 | ||
|
b591df55b0 | ||
|
bddf31443c | ||
|
b9e0e88fe9 | ||
|
5120d9ec33 | ||
|
0a423ffd40 | ||
|
505c9e8979 | ||
|
75deb02961 | ||
|
480211033d | ||
|
c0886cb5c6 | ||
|
294df8690c | ||
|
5374f652dd | ||
|
fb72ac9904 | ||
|
b6639d9e7e | ||
|
74cb79252c | ||
|
6cd3c93472 | ||
|
eb63e75379 | ||
|
ca9321624c | ||
|
40f18df90f | ||
|
0d63cfd6c3 | ||
|
3d6b22de6c | ||
|
fc233fcdd3 | ||
|
20370d4348 | ||
|
0f90671241 | ||
|
a866c4e388 | ||
|
e7241a989c | ||
|
6e788668f9 | ||
|
b246dba7e7 | ||
|
f98de3d5db | ||
|
fab7d60373 | ||
|
b4e5358145 | ||
|
486db1e797 | ||
|
78cb43d0dc | ||
|
870454330d | ||
|
509cfd15f2 | ||
|
2061daa902 | ||
|
629ca970a1 | ||
|
c8ad6f23a8 | ||
|
d8912fd0a7 | ||
|
925c80edd4 | ||
|
2f6afd375b | ||
|
d92646324c | ||
|
c9fe62a691 | ||
|
cb52ad3ba3 | ||
|
2ec4acfe52 | ||
|
1c64f1d0cf | ||
|
48f4f44289 | ||
|
80c19cbf7d | ||
|
c97cfde9f9 | ||
|
7b26b308ad | ||
|
4c10634d85 | ||
|
3ba7d7640f | ||
|
907995a221 | ||
|
7bf0985a57 | ||
|
3ca21d8c8a | ||
|
a3a87c8883 | ||
|
de4401823e | ||
|
3ee10f5983 | ||
|
0fc8ac8d4d | ||
|
41bb53a29f | ||
|
cdee6d55d3 | ||
|
e5a1afaa26 | ||
|
f50cbe74cb | ||
|
5dbbf91917 | ||
|
7424a29960 | ||
|
eb22ca2467 | ||
|
ddbcbe5458 | ||
|
6e54cfd2ac | ||
|
fc3d4b3def | ||
|
df55b2c516 | ||
|
4d9fb57e22 | ||
|
3ed08b5c39 | ||
|
d715471426 | ||
|
486a4929da | ||
|
aacddb745b | ||
|
924f499303 | ||
|
e863a200e8 | ||
|
bb7f8ae69d | ||
|
33b6fe72da | ||
|
733919be4a | ||
|
4333e5487b | ||
|
22ead933b6 | ||
|
d53a3af191 | ||
|
08e7b7e0ad | ||
|
68cb0782c0 | ||
|
0e5a8e0033 | ||
|
4945f33254 | ||
|
3fa05293fc | ||
|
27e5f2798d | ||
|
291ca0874a | ||
|
96f2aa1803 | ||
|
c6857501aa | ||
|
3aba1607b2 | ||
|
f667298b64 | ||
|
0f4c8d4923 | ||
|
6d2c5b2312 | ||
|
a95ce11ca6 | ||
|
808503d526 | ||
|
d4b6fb9214 | ||
|
9bce5a09f3 | ||
|
bd61f38169 | ||
|
cac9c63325 | ||
|
e6d555ac31 | ||
|
fd0dd9f54c | ||
|
88c161769d | ||
|
01bf1ae92d | ||
|
9193c71cff | ||
|
db278d81e4 | ||
|
bdad454a0d | ||
|
ac8064c754 | ||
|
736e963cef | ||
|
b191cd73a7 | ||
|
86d3ca48ae | ||
|
fd7e4f2268 | ||
|
e9475a9739 | ||
|
d4270e02e9 | ||
|
b9a263ecb2 | ||
|
4929d415a6 | ||
|
b9e1f518aa | ||
|
0caa44e979 | ||
|
ca56ca5bd8 | ||
|
a0f0dff88e | ||
|
e5cc38a210 | ||
|
6cb5bb7200 | ||
|
563696e291 | ||
|
c755b3c49e | ||
|
b9f3493dbc | ||
|
8c08e9e473 | ||
|
c2930b0ca5 | ||
|
4300759287 | ||
|
fc1854cadd | ||
|
933973b12c | ||
|
5b54b9cb11 | ||
|
d2f815bba7 | ||
|
a5a067d50f | ||
|
699e299345 | ||
|
05e114173d | ||
|
d958b3ff65 | ||
|
0d7e06a141 | ||
|
633095aee1 | ||
|
a4aeb9a1dd | ||
|
71005e1db3 | ||
|
d7d6d6f991 | ||
|
5986121cfc | ||
|
c452a4569e | ||
|
a182a208dc | ||
|
d273b4b48b | ||
|
afed7d4af0 | ||
|
b429e890ad | ||
|
2f976504e8 | ||
|
b5fc88227b | ||
|
4b7b859db9 | ||
|
f7d0de53bb | ||
|
603ec997ba | ||
|
f7266ef4c8 | ||
|
3d43e1568c | ||
|
3cf6a65da9 | ||
|
c1a3f003e8 | ||
|
3f61aea7fc | ||
|
bd68db51e0 | ||
|
b8a5ed710e | ||
|
34be601dd7 | ||
|
1d64ad1ccd | ||
|
dfb4ac0365 | ||
|
f2d7a3d26d | ||
|
6d415a7384 | ||
|
0ef8832b04 | ||
|
e42a037b7d | ||
|
fe18d69b65 | ||
|
a1a9a7fa9e | ||
|
07029f93e3 | ||
|
e99ef9c093 | ||
|
fef9ab674e | ||
|
ee37c37cab | ||
|
b636c1e1f8 | ||
|
8b9f0487c0 | ||
|
a6cbd5a2fd | ||
|
ece1667fb0 | ||
|
50bd8b4a09 | ||
|
93d91353a1 | ||
|
8dc9143b34 | ||
|
a0d9a1133c | ||
|
7740e4268c | ||
|
f36e4ba336 | ||
|
3df2ef8587 | ||
|
354309fcad | ||
|
e832bfc61e | ||
|
99057ed859 | ||
|
c47c5e466f | ||
|
edd00e8e70 | ||
|
82e7de2aaa | ||
|
b723714c0c | ||
|
a2903b6e63 | ||
|
9a1876571b | ||
|
c07d7165ab | ||
|
d695c5972f | ||
|
bbc09ed313 | ||
|
1d21b0da9a | ||
|
617a147706 | ||
|
d4dccfdb2d | ||
|
035e4bf727 | ||
|
9ec5bbd560 | ||
|
34482c5ed6 | ||
|
7d414b5628 | ||
|
d9528dfd09 | ||
|
12f5f8ba00 | ||
|
db0f3307e0 | ||
|
9c83825cb8 | ||
|
7a22471787 | ||
|
7548d9a8fe | ||
|
26789f9b36 | ||
|
b4524839bb | ||
|
6605c269cf | ||
|
c30478bf4a | ||
|
d986746ef9 | ||
|
a8fa061f2e | ||
|
72015b0226 | ||
|
f8e9726922 | ||
|
884363bd05 | ||
|
bdd240ecb6 | ||
|
04da292df9 | ||
|
85b8676b8e | ||
|
114bc13c23 | ||
|
5fef5f1ed4 | ||
|
807b60b0e6 | ||
|
fbc800e556 | ||
|
1991ee7a7d | ||
|
ffbfadbccf | ||
|
c3e2bce956 | ||
|
0c9520d7e3 | ||
|
1e7e5230cc | ||
|
f1efc97357 | ||
|
fb5ac6d6d7 | ||
|
4e7ca51beb | ||
|
d561600a31 | ||
|
024e843998 | ||
|
126f275e18 | ||
|
c65e5ff8e0 | ||
|
ef13e67572 | ||
|
7920fcbb5e | ||
|
fa36fdeb03 | ||
|
c143b5ccb4 | ||
|
05c8406fca | ||
|
69e3a45083 | ||
|
45259b6ec6 | ||
|
d6fe48112c | ||
|
dcf6e6b14d | ||
|
908697a963 | ||
|
bdaf961196 | ||
|
6bd5d9b1a4 | ||
|
beb0dae5a7 | ||
|
fad2d6b1d1 | ||
|
9cd830b6aa | ||
|
d9a16b0ff4 | ||
|
cb5c94ef5e | ||
|
85fbd66871 | ||
|
3da75d6125 | ||
|
d4a3a5c180 | ||
|
65e0fcbf10 | ||
|
e22aca49c8 | ||
|
71e3a473d6 | ||
|
c2cfc09f63 | ||
|
cbfad28f7e | ||
|
6a4a468022 | ||
|
01f10b56e8 | ||
|
90ec0a610e | ||
|
42bff1ce1b | ||
|
e3c7a1f31f | ||
|
0debbffa70 | ||
|
473b58d26d | ||
|
7e9498f04c | ||
|
300001e766 | ||
|
21fc6344bf | ||
|
a38edd891f | ||
|
794fa21137 | ||
|
977d1d1998 | ||
|
cde003bc98 | ||
|
9e4e1d1cb2 | ||
|
60142cd960 | ||
|
f9570a82cc | ||
|
36285ead57 | ||
|
13e488dace | ||
|
f169da8fd0 | ||
|
60741298b7 | ||
|
f73734acb0 | ||
|
fc360abe43 | ||
|
3996e11425 | ||
|
3a4fe3e391 | ||
|
98db002770 | ||
|
7c89b6934a | ||
|
380a19274d | ||
|
f1c1caf7bd | ||
|
e2997b8135 | ||
|
4ed6e4d016 | ||
|
ac8d24a1ce | ||
|
fc776921d5 | ||
|
55a653aca4 | ||
|
0d6a6b97f9 | ||
|
469ff45f01 | ||
|
bc077acfb8 | ||
|
4269626f5d | ||
|
8fa897aadb | ||
|
f549618d12 | ||
|
2fa47aaf31 | ||
|
b0874fb23a | ||
|
fb70fd77e8 | ||
|
01f17f9cbb | ||
|
4c853defb2 | ||
|
bd0db56ba0 | ||
|
95f0b3710d | ||
|
e0ac109dd1 | ||
|
86349527e7 | ||
|
18005ceee8 | ||
|
7653b3d088 | ||
|
911053f63f | ||
|
eb52f81a5c | ||
|
4839953328 | ||
|
aba0d93fda | ||
|
0de74c0448 | ||
|
ee927346b7 | ||
|
2f8015cbca | ||
|
f3bf7c4b38 | ||
|
e37c4d57da | ||
|
acff90c000 | ||
|
5f9e72dd71 | ||
|
be0bcb8f7d | ||
|
44c3d56439 | ||
|
519d731ddd | ||
|
119c00c22a | ||
|
12596dd697 | ||
|
cb38f5f0d7 | ||
|
b49384ffe4 | ||
|
7fb622638b | ||
|
bd0cc134bf | ||
|
476fd1f695 | ||
|
96053d13be | ||
|
c958bed418 | ||
|
2b2c240d39 | ||
|
1f0ae16216 | ||
|
b79392ba2c | ||
|
8ee98f0a4a | ||
|
82e78fb651 | ||
|
d0826b2c33 | ||
|
ff588200c0 | ||
|
5d6072524c | ||
|
07a66a70fc | ||
|
69ba8a3c2f | ||
|
a30c75ef71 | ||
|
d0b3727c5d | ||
|
6e94bf5b6d | ||
|
0f04e270a7 | ||
|
ec4f5007e7 | ||
|
21b906e0e0 | ||
|
7ebaf8e843 | ||
|
640398ced4 | ||
|
ebbe1fc236 | ||
|
aac8e45397 | ||
|
5ebfa5ecf7 | ||
|
12e041c9ef | ||
|
54913f06a3 | ||
|
44a36368a2 | ||
|
8f9ff930b3 | ||
|
e199f6db87 | ||
|
c83dd3ccd7 | ||
|
78fbafa1cd | ||
|
c0012540ed | ||
|
9ab6df0e54 | ||
|
685b775b68 | ||
|
9056a5a7b6 | ||
|
e1c56bcbfe | ||
|
15d68467a1 | ||
|
3b6ecc573e | ||
|
9427b1e594 | ||
|
3ca3eaa62c | ||
|
dda448e050 | ||
|
60873144ea | ||
|
a9d17c96be | ||
|
aaee895b2b | ||
|
7d7e17b351 | ||
|
c71a976e76 | ||
|
3623183072 | ||
|
e6bd979aeb | ||
|
266c160108 | ||
|
b5b6350cc7 | ||
|
bbcf4800de | ||
|
41056ace02 | ||
|
60a80dd678 | ||
|
0efda04920 | ||
|
8819e58882 | ||
|
28eeaf201b | ||
|
74bfcea6a8 | ||
|
16f1d7fad9 | ||
|
9aa38cf0ae | ||
|
a7fa7466fb | ||
|
3f73c61cee | ||
|
a64f4cd871 | ||
|
a41eff1276 | ||
|
b88fe572fe | ||
|
59ba6f8aec | ||
|
25343da6b7 | ||
|
60d17b97f5 | ||
|
8cb72d87e4 | ||
|
8872594ab9 | ||
|
b50584119b | ||
|
975f4f2a17 | ||
|
16e4f79f09 | ||
|
76cae20c33 | ||
|
b66f23cfd0 | ||
|
27d6e5d8cf | ||
|
ff5d5b97c3 | ||
|
ce46c4dec4 | ||
|
77e8be09a1 | ||
|
013d77488a | ||
|
29ad2496b6 | ||
|
97a013b2b7 | ||
|
77808223dc | ||
|
0ad704e532 | ||
|
2220d2dab8 | ||
|
97c44042e1 | ||
|
a03caea549 | ||
|
737b510116 | ||
|
47cba83450 |
38
.editorconfig
Normal file
38
.editorconfig
Normal file
@@ -0,0 +1,38 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = tab
|
||||
tab_width = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
[*.{js,py}]
|
||||
charset = utf-8
|
||||
|
||||
# 4 space indentation
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
# Indentation override for all JS under lib directory
|
||||
[scripts/**.js]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
38
.gitattributes
vendored
Normal file
38
.gitattributes
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# FROM https://github.com/libgit2/libgit2sharp
|
||||
# Text files that should be normalized to LF in odb.
|
||||
*.cs text diff=csharp
|
||||
*.config text
|
||||
|
||||
*.sln text
|
||||
*.csproj text
|
||||
|
||||
*.md text
|
||||
*.sh text
|
||||
*.ps1 text
|
||||
*.cmd text
|
||||
*.bat text
|
||||
*.markdown text
|
||||
*.msbuild text
|
||||
|
||||
Lib/* binary
|
||||
GitHub.Tests.Integration/Resources/* binary
|
||||
|
||||
|
||||
# Binary files that should not be normalized or diffed
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.gif binary
|
||||
|
||||
*.pfx binary
|
||||
*.snk binary
|
||||
*.dll binary
|
||||
*.exe binary
|
||||
*.lib binary
|
||||
*.exp binary
|
||||
*.pdb binary
|
||||
*.sdf binary
|
||||
*.7z binary
|
||||
|
||||
|
||||
# Catch all for anything we forgot. Add rules if you get CRLF -> LF warnings.
|
||||
* text eol=lf
|
51
.github/ISSUE_TEMPLATE.md
vendored
51
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,42 +1,37 @@
|
||||
_This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._
|
||||
**In raising this issue, I confirm the following:** `{please fill the checkboxes, e.g: [X]}`
|
||||
|
||||
**In raising this issue, I confirm the following (please check boxes, eg [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 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)).
|
||||
|
||||
- [ ] 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'm 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 the source code relevant to this issue?:**
|
||||
|
||||
**How familiar are you with the codebase?:**
|
||||
|
||||
- [ ] 1 (very unfamiliar)
|
||||
- [ ] 2
|
||||
- [ ] 3
|
||||
- [ ] 4
|
||||
- [ ] 5
|
||||
- [ ] 6
|
||||
- [ ] 7
|
||||
- [ ] 8
|
||||
- [ ] 9
|
||||
- [ ] 10 (very familiar)
|
||||
`{Replace this with a number from 1 to 10. 1 being not familiar, and 10 being very familiar}`
|
||||
|
||||
---
|
||||
**[FEATURE REQUEST | QUESTION | OTHER]:**
|
||||
**Expected behaviour:**
|
||||
|
||||
_{replace this section with your content or delete if not a FEATURE REQUEST/QUESTION/OTHER}_
|
||||
`{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 Log generated by `pihole -d`:**
|
||||
**Troubleshooting undertaken, and/or other relevant information:**
|
||||
|
||||
`http://termbin.com/<something>`
|
||||
`{Steps of what you have done to fix this}`
|
||||
|
||||
> * `{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)._
|
||||
|
48
.github/PULL_REQUEST_TEMPLATE.md
vendored
48
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,25 +1,31 @@
|
||||
_This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._
|
||||
**By submitting this pull request, I confirm the following:**
|
||||
*please fill any appropriate checkboxes, e.g: [X]*
|
||||
|
||||
**By submitting this pull request, I confirm the following (please check boxes, eg [X]):**
|
||||
- [ ] 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?:**
|
||||
|
||||
- [ ] 1 (very unfamiliar)
|
||||
- [ ] 2
|
||||
- [ ] 3
|
||||
- [ ] 4
|
||||
- [ ] 5
|
||||
- [ ] 6
|
||||
- [ ] 7
|
||||
- [ ] 8
|
||||
- [ ] 9
|
||||
- [ ] 10 (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*
|
||||
|
||||
|
||||
**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.
|
||||
|
||||
|
70
.gitignore
vendored
70
.gitignore
vendored
@@ -1 +1,71 @@
|
||||
.DS_Store
|
||||
*.pyc
|
||||
*.swp
|
||||
__pycache__
|
||||
.cache
|
||||
|
||||
# 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
|
||||
|
25
.idea/codeStyleSettings.xml
generated
Normal file
25
.idea/codeStyleSettings.xml
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectCodeStyleSettingsManager">
|
||||
<option name="PER_PROJECT_SETTINGS">
|
||||
<value>
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="8" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="false" />
|
||||
<option name="SMART_TABS" value="false" />
|
||||
<option name="LABEL_INDENT_SIZE" value="0" />
|
||||
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
|
||||
<option name="USE_RELATIVE_INDENTS" value="false" />
|
||||
</value>
|
||||
</option>
|
||||
<MarkdownNavigatorCodeStyleSettings>
|
||||
<option name="RIGHT_MARGIN" value="72" />
|
||||
</MarkdownNavigatorCodeStyleSettings>
|
||||
</value>
|
||||
</option>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</component>
|
||||
</project>
|
13
.idea/codeStyles/Project.xml
generated
Normal file
13
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</value>
|
||||
</option>
|
||||
<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,19 +1,38 @@
|
||||
approve_by_comment: true
|
||||
approve_regex: '^(Approved|:shipit:|:\+1:|Engage)'
|
||||
reject_regex: '^(Rejected|:-1:|Borg)'
|
||||
reset_on_push: true
|
||||
author_approval: required
|
||||
reviewers:
|
||||
members:
|
||||
- brantje
|
||||
- dschaper
|
||||
- jacobsalmela
|
||||
- Mcat12
|
||||
- PromoFaux
|
||||
name: pullapprove
|
||||
required: 3
|
||||
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
|
||||
|
3
.stickler.yml
Normal file
3
.stickler.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
linters:
|
||||
shellcheck:
|
||||
shell: bash
|
10
.travis.yml
Normal file
10
.travis.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
sudo: required
|
||||
services:
|
||||
- docker
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
|
||||
script: py.test -vv
|
@@ -28,10 +28,13 @@ 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
|
||||
|
||||
- Submit Pull Requests to the **development branch only**.
|
||||
- Before Submitting your Pull Request, merge `development` with your new branch and fix any conflicts. (Make sure you don't break anything in development!)
|
||||
- Please use the [Google Style Guide for Shell](https://google.github.io/styleguide/shell.xml) for your code submission styles.
|
||||
- Commit Unix line endings.
|
||||
- Please use the Pi-hole brand: **Pi-hole** (Take a special look at the capitalized 'P' and a low 'h' with a hyphen)
|
||||
- (Optional fun) keep to the theme of Star Trek/black holes/gravity.
|
||||
|
389
LICENSE
389
LICENSE
@@ -1,339 +1,146 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
Copyright (C) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
Pi-hole Core
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
This software is licensed under the European Union Public License (EUPL)
|
||||
The license is available in the 22 official languages of the EU. The English version is included here.
|
||||
Please see https://joinup.ec.europa.eu/community/eupl/og_page/eupl for official translations of the other languages.
|
||||
|
||||
Preamble
|
||||
This license applies to the whole project EXCEPT:
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
- any commits made to the master branch prior to the release of version 3.0
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
The licenses that existed prior to this change have remained intact.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
-------------------------------------------------------------
|
||||
EUROPEAN UNION PUBLIC LICENCE v. 1.2
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
EUPL © the European Union 2007, 2016
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
This European Union Public Licence (the EUPL) applies to the Work (as defined below) which is provided under the terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such use is covered by a right of the copyright holder of the Work).
|
||||
The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following notice immediately following the copyright notice for the Work:
|
||||
Licensed under the EUPL
|
||||
or has expressed by any other means his willingness to license under the EUPL.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
1. Definitions
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
- The Licence: this Licence.
|
||||
- The Original Work: the work or software distributed or communicated by the Licensor under this Licence, available as Source Code and also as Executable Code as the case may be.
|
||||
- Derivative Works: the works or software that could be created by the Licensee, based upon the Original Work or modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in the country mentioned in Article 15.
|
||||
- The Work: the Original Work or its Derivative Works.
|
||||
- The Source Code: the human-readable form of the Work which is the most convenient for people to study and modify.
|
||||
- The Executable Code: any code which has generally been compiled and which is meant to be interpreted by a computer as a program.
|
||||
- The Licensor: the natural or legal person that distributes or communicates the Work under the Licence.
|
||||
- Contributor(s): any natural or legal person who modifies the Work under the Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
- The Licensee or You: any natural or legal person who makes any usage of the Work under the terms of the Licence.
|
||||
- Distribution or Communication: any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential functionalities at the disposal of any other natural or legal person.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
2. Scope of the rights granted by the Licence
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for the duration of copyright vested in the Original Work:
|
||||
- use the Work in any circumstance and for all usage,
|
||||
- reproduce the Work,
|
||||
- modify the Work, and make Derivative Works based upon the Work,
|
||||
- communicate to the public, including the right to make available or display the Work or copies thereof to the public and perform publicly, as the case may be, the Work,
|
||||
- distribute the Work or copies thereof,
|
||||
- lend and rent the Work or copies thereof,
|
||||
- sublicense rights in the Work or copies thereof.
|
||||
Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the applicable law permits so.
|
||||
In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed by law in order to make effective the licence of the economic rights here above listed.
|
||||
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the extent necessary to make use of the rights granted on the Work under this Licence.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
3. Communication of the Source Code
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to distribute or communicate the Work.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
4. Limitations on copyright
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations thereto.
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
5. Obligations of the Licensee
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work to carry prominent notices stating that the Work has been modified and the date of modification.
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless the Original Work is expressly distributed only under this version of the Licence - for example by communicating EUPL v. 1.2 only. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the Work or Derivative Work that alter or restrict the terms of the Licence.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done under the terms of this Compatible Licence. For the sake of this clause, Compatible Licence refers to the licences listed in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available for as long as the Licensee continues to distribute or communicate the Work.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the copyright notice.
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
6. Chain of Authorship
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence.
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence.
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions to the Work, under the terms of this Licence.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
7. Disclaimer of Warranty
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work and may therefore contain defects or bugs inherent to this type of development.
|
||||
For the above reason, the Work is provided under the Licence on an as is basis and without warranties of any kind concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this Licence.
|
||||
This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
8. Disclaimer of Liability
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, the Licensor will be liable under statutory product liability laws as far such laws apply to the Work.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
9. Additional agreements
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by the fact You have accepted any warranty or additional liability.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
10. Acceptance of the Licence
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
The provisions of this Licence can be accepted by clicking on an icon I agree placed under the bottom of a window displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms and conditions.
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution or Communication by You of the Work or copies thereof.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
11. Information to the public
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, by offering to download the Work from a remote location) the distribution channel or media (for example, a website) must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence and the way it may be accessible, concluded, stored and reproduced by the Licensee.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
12. Termination of the Licence
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms of the Licence.
|
||||
Such a termination will not terminate the licences of any person who has received the Work from the Licensee under the Licence, provided such persons remain in full compliance with the Licence.
|
||||
|
||||
NO WARRANTY
|
||||
13. Miscellaneous
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the Work.
|
||||
If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid and enforceable.
|
||||
The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. New versions of the Licence will be published with a unique version number.
|
||||
All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take advantage of the linguistic version of their choice.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
14. Jurisdiction
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
Without prejudice to specific agreement between parties,
|
||||
- any litigation resulting from the interpretation of this License, arising between the European Union institutions, bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union,
|
||||
- any litigation arising between other parties and resulting from the interpretation of this License, will be subject to the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business.
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
15. Applicable Law
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
Without prejudice to specific agreement between parties,
|
||||
- this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, resides or has his registered office,
|
||||
- this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside a European Union Member State.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
===
|
||||
|
||||
{description}
|
||||
Copyright (C) {year} {fullname}
|
||||
Appendix
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
Compatible Licences according to Article 5 EUPL are:
|
||||
- GNU General Public License (GPL) v. 2, v. 3
|
||||
- GNU Affero General Public License (AGPL) v. 3
|
||||
- Open Software License (OSL) v. 2.1, v. 3.0
|
||||
- Eclipse Public License (EPL) v. 1.0
|
||||
- CeCILL v. 2.0, v. 2.1
|
||||
- Mozilla Public Licence (MPL) v. 2
|
||||
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software
|
||||
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||
- Québec Free and Open-Source Licence - Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+)
|
||||
- The European Commission may update this Appendix to later versions of the above licences without producing a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the covered Source Code from exclusive appropriation.
|
||||
- All other changes or additions to this Appendix require the production of a new EUPL version.
|
||||
|
262
README.md
262
README.md
@@ -1,125 +1,217 @@
|
||||
[](https://www.bountysource.com/trackers/3011939-pi-hole-pi-hole?utm_source=3011939&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
||||
<p align="center">
|
||||
<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>
|
||||
|
||||
# Automated Install
|
||||
The Pi-hole is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) that protects your devices from unwanted content, without installing any client-side software.
|
||||
|
||||
Designed For Raspberry Pi A+, B, B+, 2, 3B, and Zero (with an Ethernet/Wi-Fi adapter)
|
||||
Works on most Debian and CentOS/RHEL based distributions!
|
||||
- **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
|
||||
|
||||
1. Install Raspbian
|
||||
2. Run the command below (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!)
|
||||
-----
|
||||
<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>
|
||||
|
||||
## `curl -L https://install.pi-hole.net | bash`
|
||||
## One-Step Automated Install
|
||||
Those who want to get started quickly and conveniently, may install Pi-hole using the following command:
|
||||
|
||||
### Alternative Semi-Automated install
|
||||
#### `curl -sSL https://install.pi-hole.net | bash`
|
||||
|
||||
```bash
|
||||
## 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/"
|
||||
sudo bash basic-install.sh
|
||||
```
|
||||
|
||||
### Method 2: Manually download the installer and run
|
||||
```
|
||||
wget -O basic-install.sh https://install.pi-hole.net
|
||||
chmod +x basic-install.sh
|
||||
./basic-install.sh
|
||||
sudo bash basic-install.sh
|
||||
```
|
||||
|
||||
If you wish to read over the script before running it, then after the `wget` command, do `nano basic-install.sh` to open a text viewer
|
||||
## Post-install: Make your network take advantage of Pi-hole
|
||||
|
||||
Once installed, [configure your router to have **DHCP clients use the Pi as their DNS server**](http://pi-hole.net/faq/can-i-set-the-pi-hole-to-be-the-dns-server-at-my-router-so-i-dont-have-to-change-settings-for-my-devices/) 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 the Raspberry Pi as its DNS server](http://pi-hole.net/faq/how-do-i-use-the-pi-hole-as-my-dns-server/).
|
||||
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.
|
||||
|
||||
## How To Install Pi-hole
|
||||
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).
|
||||
|
||||
[](https://www.youtube.com/watch?v=TzFLJqUeirA)
|
||||
As a last resort, you can always manually set each device to use Pi-hole as their DNS server.
|
||||
|
||||
## How It Works
|
||||
-----
|
||||
|
||||
**Watch the 60-second video below to get a quick overview**
|
||||
## 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.
|
||||
|
||||
[](https://youtu.be/9Eti3xibiho)
|
||||
Make no mistake: **your support is absolutely vital to help keep us innovating!**
|
||||
|
||||
## Pi-hole Is Free, But Powered By Your Donations
|
||||
### Donations
|
||||
Sending a donation using our links below is **extremely helpful** in offsetting a portion of our monthly expenses:
|
||||
|
||||
Send a one-time donation or sign up for Optimal.com's service using our link below to provide us with a small portion of the monthly fee.
|
||||
<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 Address: <code>1GKnevUnVaQM2pQieMyeHkpr8DXfkpfAtL</code>
|
||||
|
||||
-  : [Donate](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY)
|
||||
-  : [Donate](https://flattr.com/submit/auto?user_id=jacobsalmela&url=https://github.com/pi-hole/pi-hole)
|
||||
-  : 1GKnevUnVaQM2pQieMyeHkpr8DXfkpfAtL
|
||||
### Alternative support
|
||||
If you'd rather not donate (_which is okay!_), there are other ways you can help support us:
|
||||
|
||||
## Get Help Or Connect With Us On The Web
|
||||
- [Digital Ocean](http://www.digitalocean.com/?refcode=344d234950e1) affiliate link
|
||||
- [Vultr](http://www.vultr.com/?ref=7190426) affiliate link
|
||||
- [UNIXstickers.com](http://unixstickers.refr.cc/jacobs) affiliate link
|
||||
- [Pi-hole Swag Store](https://pi-hole.net/shop/)
|
||||
- Spreading the word about our software, and how you have benefited from it
|
||||
|
||||
- [@The_Pi_Hole](https://twitter.com/The_Pi_Hole)
|
||||
- [/r/pihole](https://www.reddit.com/r/pihole/)
|
||||
- [Pi-hole YouTube channel](https://www.youtube.com/channel/UCT5kq9w0wSjogzJb81C9U0w)
|
||||
- [Wiki](https://github.com/pi-hole/pi-hole/wiki/Customization)
|
||||
- [FAQs](https://pi-hole.net/help/)
|
||||
- [](https://gitter.im/pi-hole/pi-hole?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
### Contributing via GitHub
|
||||
We welcome _everyone_ to contribute to issue reports, suggest new features, and create pull requests.
|
||||
|
||||
## Technical Details
|
||||
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 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. You can also [replace ads with any image you want](http://pi-hole.net/faq/is-it-possible-to-change-the-blank-page-that-takes-place-of-the-ads-to-something-else/) since it is just a simple Webpage taking place of the ads.
|
||||
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.
|
||||
|
||||
### Gravity
|
||||
### 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 [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)).
|
||||
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!
|
||||
|
||||
## Web Interface
|
||||
-----
|
||||
|
||||
The [Web interface](https://github.com/jacobsalmela/AdminLTE#pi-hole-admin-dashboard) will be installed automatically so you can view stats and change settings. You can find it at:
|
||||
## 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.
|
||||
|
||||
`http://192.168.1.x/admin/index.php` or `http://pi.hole/admin`
|
||||
<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>
|
||||
</ul>
|
||||
<br/>
|
||||
<ul>
|
||||
<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>
|
||||
|
||||

|
||||
-----
|
||||
|
||||
### Whitelist and blacklist
|
||||
## 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 understanding of `bash`.
|
||||
|
||||
Domains can be whitelisted and blacklisted using two pre-installed scripts. See [the wiki page](https://github.com/pi-hole/pi-hole/wiki/Whitelisting-and-Blacklisting) for more details 
|
||||
<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>
|
||||
|
||||
## API
|
||||
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!
|
||||
|
||||
A basic read-only API can be accessed at `/admin/api.php`. It returns the following JSON:
|
||||
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.
|
||||
|
||||
```json
|
||||
{
|
||||
"domains_being_blocked": "136708",
|
||||
"dns_queries_today": "18108",
|
||||
"ads_blocked_today": "14648",
|
||||
"ads_percentage_today": "80.89"
|
||||
}
|
||||
```
|
||||
### 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!
|
||||
|
||||
The same output can be achieved on the CLI by running `chronometer.sh -j`
|
||||
<a href="https://pi-hole.github.io/graphics/Screenshots/dashboard.png"><img src="https://pi-hole.github.io/graphics/Screenshots/dashboard.png" width="888" height="522" alt="Pi-hole Dashboard"/></a>
|
||||
|
||||
## Real-time Statistics
|
||||
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!
|
||||
|
||||
You can view [real-time stats](http://pi-hole.net/faq/install-the-real-time-lcd-monitor-chronometer/) 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). 
|
||||
There are several ways to [access the dashboard](https://discourse.pi-hole.net/t/how-do-i-access-pi-holes-dashboard-admin-interface/3168):
|
||||
|
||||
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 Faster-Than-Light Engine
|
||||
The [FTL Engine](https://github.com/pi-hole/FTL) 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, FTL does this all *very quickly*!
|
||||
|
||||
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
|
||||
|
||||
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).
|
||||
|
||||
-----
|
||||
|
||||
## The Origin Of Pi-hole
|
||||
Pi-hole being a **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) - webserver 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 setup 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.
|
||||
|
||||
-----
|
||||
|
||||
## Pi-hole Projects
|
||||
|
||||
- [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://www.stinebaugh.info/get-led-alerts-for-each-blocked-ad-using-pi-hole/)
|
||||
- [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 Visualizser](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)
|
||||
- [The Big Blocklist Collection](https://wally3k.github.io)
|
||||
- [Docker Pi-hole container (x86 and ARM)](https://hub.docker.com/r/diginc/pi-hole/)
|
||||
- [Pi-Hole in the cloud](http://blog.codybunch.com/2015/07/28/Pi-Hole-in-the-cloud/)
|
||||
- [Pie in the Sky-Hole [A Pi-Hole in the cloud for ad-blocking via DNS]](https://dlaa.me/blog/post/skyhole)
|
||||
- [Pi-hole Enable/Disable Button](http://thetimmy.silvernight.org/pages/endisbutton/)
|
||||
- [Minibian Pi-hole](https://munkjensen.net/wiki/index.php/See_my_Pi-Hole#Minibian_Pi-hole)
|
||||
- [CHiP-hole: Network-wide Ad-blocker](https://www.hackster.io/jacobsalmela/chip-hole-network-wide-ad-blocker-98e037)
|
||||
- [Chrome Extension: Pi-Hole List Editor](https://chrome.google.com/webstore/detail/pi-hole-list-editor/hlnoeoejkllgkjbnnnhfolapllcnaglh) ([Source Code](https://github.com/packtloss/pihole-extension))
|
||||
- [Splunk: Pi-hole Visualiser](https://splunkbase.splunk.com/app/3023/)
|
||||
- [Adblocking with Pi-hole and Ubuntu 14.04 on VirtualBox](https://hbalagtas.blogspot.com.au/2016/02/adblocking-with-pi-hole-and-ubuntu-1404.html)
|
||||
- [Pi-hole stats in your Mac's menu bar](https://getbitbar.com/plugins/Network/pi-hole.1m.py)
|
||||
- [Pi-hole unRAID Template](https://forums.lime-technology.com/topic/36810-support-spants-nodered-mqtt-dashing-couchdb/)
|
||||
- [Copernicus: Windows Tray Application](https://github.com/goldbattle/copernicus)
|
||||
- [Let your blink1 device blink when Pi-hole filters ads](https://gist.github.com/elpatron68/ec0b4c582e5abf604885ac1e068d233f)
|
||||
- [Pi-hole metrics](https://github.com/nlamirault/pihole_exporter) exporter for [Prometheus](https://prometheus.io/)
|
||||
- [Magic Mirror with DNS Filtering](https://zonksec.com/blog/magic-mirror-dns-filtering/#dnssoftware)
|
||||
- [Pi-hole Droid: 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)
|
||||
-----
|
||||
|
||||
## Coverage
|
||||
|
||||
- [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/)
|
||||
|
||||
## Other Operating Systems
|
||||
|
||||
This script will work for other UNIX-like systems with some slight **modifications**. As long as you can install `dnsmasq` and a Webserver, it should work OK. The automated install is only for a clean install of a Debian based system, such as the Raspberry Pi.
|
||||
- [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/)
|
||||
- [MakeUseOf: Adblock Everywhere: The Raspberry Pi-Hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/)
|
||||
- [Catchpoint: Ad-Blocking on Apple iOS9: Valuing the End User Experience](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/)
|
||||
- [Security Now Netcast: Pi-hole](https://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s)
|
||||
- [TekThing: Raspberry Pi-Hole Makes Ads Disappear!](https://youtu.be/8Co59HU2gY0?t=2m)
|
||||
- [Foolish Tech Show](https://youtu.be/bYyena0I9yc?t=2m4s)
|
||||
- [Block Ads on All Home Devices for $53.18](https://medium.com/@robleathern/block-ads-on-all-home-devices-for-53-18-a5f1ec139693#.gj1xpgr5d)
|
||||
- [Pi-Hole for Ubuntu 14.04](http://www.boyter.org/2015/12/pi-hole-ubuntu-14-04/)
|
||||
- [MacObserver Podcast 585](https://www.macobserver.com/tmo/podcast/macgeekgab-585)
|
||||
- [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)
|
||||
- [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/)
|
||||
- [Digital Trends: 5 Fun, Easy Projects You Can Try With a $35 Raspberry Pi](https://youtu.be/QwrKlyC2kdM?t=1m42s)
|
||||
- [Adafruit: Raspberry Pi Quick Look at Pi Hole ad blocking server with Tony D](https://www.youtube.com/watch?v=eg4u2j1HYlI)
|
||||
- [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/)
|
||||
- [Linux Pro: The Hole Truth](http://www.linuxpromagazine.com/Issues/2017/200/The-sysadmin-s-daily-grind-Pi-hole)
|
||||
- [CryptoAUSTRALIA: How We Tried 5 Privacy Focused Raspberry Pi Projects](https://blog.cryptoaustralia.org.au/2017/10/05/5-privacy-focused-raspberry-pi-projects/)
|
||||
- [CryptoAUSTRALIA: Pi-hole Workshop](https://blog.cryptoaustralia.org.au/2017/11/02/pi-hole-network-wide-ad-blocker/)
|
||||
- [Know How 355: Killing ads with a Raspberry Pi-Hole!](https://www.twit.tv/shows/know-how/episodes/355)
|
||||
|
@@ -1,56 +1,23 @@
|
||||
## Pi-hole ad-list default sources. Updated 22/05/2016 #########################
|
||||
# #
|
||||
# To make changes to this file: #
|
||||
# 1. run `cp /etc/pihole/adlists.default /etc/pihole/adlists.list` #
|
||||
# 2. run `nano /etc/pihole/adlists.list` #
|
||||
# 3. Uncomment or comment any of the below lists #
|
||||
# #
|
||||
# Know of any other lists? Feel free to let us know about them, or add them #
|
||||
# to this file! #
|
||||
################################################################################
|
||||
|
||||
# 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
|
||||
|
||||
# Other lists we consider safe:
|
||||
http://mirror1.malwaredomains.com/files/justdomains
|
||||
##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 list. Updated frequently, but has been known to block legitimate sites.
|
||||
##Hosts-file.net
|
||||
https://hosts-file.net/ad_servers.txt
|
||||
|
||||
# Mahakala list. Has been known to block legitimage domains including the entire .com range.
|
||||
# Warning: Due to the sheer size of this list, the web admin console will be unresponsive.
|
||||
#http://adblock.mahakala.is/
|
||||
|
||||
# ADZHOSTS list. Has been known to block legitimate domains
|
||||
#http://optimate.dl.sourceforge.net/project/adzhosts/HOSTS.txt
|
||||
|
||||
# Windows 10 telemetry list
|
||||
#https://raw.githubusercontent.com/crazy-max/WindowsSpyBlocker/master/data/hosts/win10/spy.txt
|
||||
|
||||
# Securemecca.com list - Also blocks "adult" sites (pornography/gambling etc)
|
||||
#http://securemecca.com/Downloads/hosts.txt
|
||||
|
||||
# Quidsup's tracker list
|
||||
https://raw.githubusercontent.com/quidsup/notrack/master/trackers.txt
|
||||
|
||||
# Block the BBC News website Breaking News banner
|
||||
#https://raw.githubusercontent.com/BreakingTheNews/BreakingTheNews.github.io/master/hosts
|
||||
|
||||
# List of known C&C malware servers (see https://github.com/pi-hole/pi-hole/issues/528)
|
||||
https://ransomwaretracker.abuse.ch/downloads/RW_DOMBL.txt
|
||||
|
||||
# Untested Lists:
|
||||
#https://raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt
|
||||
#https://raw.githubusercontent.com/Dawsey21/Lists/master/main-blacklist.txt
|
||||
#http://malwaredomains.lehigh.edu/files/domains.txt
|
||||
# Following two lists should be used simultaneously: (readme https://github.com/notracking/hosts-blocklists/)
|
||||
#https://raw.github.com/notracking/hosts-blocklists/master/hostnames.txt
|
||||
#https://raw.github.com/notracking/hosts-blocklists/master/domains.txt
|
||||
# Combination of serveral host files on the internet (warning some facebook domains are also blocked but you can go to facebook.com). See https://github.com/mat1th/Dns-add-block for more information.
|
||||
#https://raw.githubusercontent.com/mat1th/Dns-add-block/master/hosts
|
||||
|
@@ -9,53 +9,39 @@
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# If you want dnsmasq to read another file, as well as /etc/hosts, use
|
||||
# this.
|
||||
###############################################################################
|
||||
# FILE AUTOMATICALLY POPULATED BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
|
||||
# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
|
||||
# #
|
||||
# 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 #
|
||||
###############################################################################
|
||||
|
||||
addn-hosts=/etc/pihole/gravity.list
|
||||
addn-hosts=/etc/pihole/black.list
|
||||
addn-hosts=/etc/pihole/local.list
|
||||
|
||||
# The following two options make you a better netizen, since they
|
||||
# tell dnsmasq to filter out queries which the public DNS cannot
|
||||
# answer, and which load the servers (especially the root servers)
|
||||
# unnecessarily. If you have a dial-on-demand link they also stop
|
||||
# these requests from bringing up the link unnecessarily.
|
||||
|
||||
# Never forward plain names (without a dot or domain part)
|
||||
domain-needed
|
||||
# Never forward addresses in the non-routed address spaces.
|
||||
|
||||
localise-queries
|
||||
|
||||
bogus-priv
|
||||
|
||||
# If you don't want dnsmasq to read /etc/resolv.conf or any other
|
||||
# file, getting its servers from this file instead (see below), then
|
||||
# uncomment this.
|
||||
no-resolv
|
||||
|
||||
# Add other name servers here, with domain specs if they are for
|
||||
# non-public domains.
|
||||
server=@DNS1@
|
||||
server=@DNS2@
|
||||
|
||||
# If you want dnsmasq to listen for DHCP and DNS requests only on
|
||||
# specified interfaces (and the loopback) give the name of the
|
||||
# interface (eg eth0) here.
|
||||
interface=@INT@
|
||||
# Or which to listen on by address (remember to include 127.0.0.1 if
|
||||
# you use this.)
|
||||
listen-address=127.0.0.1
|
||||
|
||||
# Set the cachesize here.
|
||||
cache-size=10000
|
||||
|
||||
# For debugging purposes, log each DNS query as it passes through
|
||||
# dnsmasq.
|
||||
log-queries
|
||||
log-queries=extra
|
||||
log-facility=/var/log/pihole.log
|
||||
|
||||
# Normally responses which come from /etc/hosts and the DHCP lease
|
||||
# file have Time-To-Live set as zero, which conventionally means
|
||||
# do not cache further. If you are happy to trade lower load on the
|
||||
# server for potentially stale date, you can set a time-to-live (in
|
||||
# seconds) here.
|
||||
local-ttl=300
|
||||
local-ttl=2
|
||||
|
||||
# This allows it to continue functioning without being blocked by syslog, and allows syslog to use dnsmasq for DNS queries without risking deadlock
|
||||
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,230 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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
|
||||
# Blacklists domains
|
||||
#
|
||||
# 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.
|
||||
|
||||
helpFunc()
|
||||
{
|
||||
echo "::: Immediately blacklists one or more domains in the hosts file"
|
||||
echo ":::"
|
||||
echo ":::"
|
||||
echo "::: Usage: pihole -b domain1 [domain2 ...]"
|
||||
echo "::: Options:"
|
||||
echo "::: -d, --delmode Remove domains from the blacklist"
|
||||
echo "::: -nr, --noreload Update blacklist without refreshing dnsmasq"
|
||||
echo "::: -f, --force Force updating of the hosts files, even if there are no changes"
|
||||
echo "::: -q, --quiet output is less verbose"
|
||||
echo "::: -h, --help Show this help dialog"
|
||||
echo "::: -l, --list Display your blacklisted domains"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
helpFunc
|
||||
fi
|
||||
|
||||
#globals
|
||||
basename=pihole
|
||||
piholeDir=/etc/${basename}
|
||||
adList=${piholeDir}/gravity.list
|
||||
blacklist=${piholeDir}/blacklist.txt
|
||||
reload=true
|
||||
addmode=true
|
||||
force=false
|
||||
verbose=true
|
||||
|
||||
domList=()
|
||||
domToRemoveList=()
|
||||
|
||||
piholeIPfile=/etc/pihole/piholeIP
|
||||
piholeIPv6file=/etc/pihole/.useIPv6
|
||||
|
||||
if [[ -f ${piholeIPfile} ]];then
|
||||
# If the file exists, it means it was exported from the installation script and we should use that value instead of detecting it in this script
|
||||
piholeIP=$(cat ${piholeIPfile})
|
||||
#rm $piholeIPfile
|
||||
else
|
||||
# Otherwise, the IP address can be taken directly from the machine, which will happen when the script is run by the user and not the installation script
|
||||
IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}')
|
||||
piholeIPCIDR=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}')
|
||||
piholeIP=${piholeIPCIDR%/*}
|
||||
fi
|
||||
|
||||
modifyHost=false
|
||||
|
||||
# After setting defaults, check if there's local overrides
|
||||
if [[ -r ${piholeDir}/pihole.conf ]];then
|
||||
echo "::: Local calibration requested..."
|
||||
. ${piholeDir}/pihole.conf
|
||||
fi
|
||||
|
||||
|
||||
if [[ -f ${piholeIPv6file} ]];then
|
||||
# If the file exists, then the user previously chose to use IPv6 in the automated installer
|
||||
piholeIPv6=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }')
|
||||
fi
|
||||
|
||||
HandleOther(){
|
||||
#check validity of domain
|
||||
validDomain=$(echo "$1" | perl -ne'print if /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/')
|
||||
if [ -z "$validDomain" ]; then
|
||||
echo "::: $1 is not a valid argument or domain name"
|
||||
else
|
||||
domList=("${domList[@]}" ${validDomain})
|
||||
fi
|
||||
}
|
||||
|
||||
PopBlacklistFile(){
|
||||
#check blacklist file exists, and if not, create it
|
||||
if [[ ! -f ${blacklist} ]];then
|
||||
touch ${blacklist}
|
||||
fi
|
||||
for dom in "${domList[@]}"; do
|
||||
if "$addmode"; then
|
||||
AddDomain "$dom"
|
||||
else
|
||||
RemoveDomain "$dom"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
AddDomain(){
|
||||
#| sed 's/\./\\./g'
|
||||
bool=false
|
||||
grep -Ex -q "$1" ${blacklist} || bool=true
|
||||
if ${bool}; then
|
||||
#domain not found in the blacklist file, add it!
|
||||
if ${verbose}; then
|
||||
echo -n "::: Adding $1 to blacklist file..."
|
||||
fi
|
||||
echo "$1" >> ${blacklist}
|
||||
modifyHost=true
|
||||
echo " done!"
|
||||
else
|
||||
if ${verbose}; then
|
||||
echo "::: $1 already exists in $blacklist! No need to add"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
RemoveDomain(){
|
||||
|
||||
bool=false
|
||||
grep -Ex -q "$1" ${blacklist} || bool=true
|
||||
if ${bool}; then
|
||||
#Domain is not in the blacklist file, no need to Remove
|
||||
if ${verbose}; then
|
||||
echo "::: $1 is NOT blacklisted! No need to remove"
|
||||
fi
|
||||
else
|
||||
#Domain is in the blacklist file, add to a temporary array
|
||||
if ${verbose}; then
|
||||
echo "::: Un-blacklisting $dom..."
|
||||
fi
|
||||
domToRemoveList=("${domToRemoveList[@]}" $1)
|
||||
modifyHost=true
|
||||
fi
|
||||
}
|
||||
|
||||
ModifyHostFile(){
|
||||
if ${addmode}; then
|
||||
#add domains to the hosts file
|
||||
if [[ -r ${blacklist} ]];then
|
||||
numberOf=$(cat ${blacklist} | sed '/^\s*$/d' | wc -l)
|
||||
plural=; [[ "$numberOf" != "1" ]] && plural=s
|
||||
echo ":::"
|
||||
echo -n "::: Modifying HOSTS file to blacklist $numberOf domain${plural}..."
|
||||
if [[ -n ${piholeIPv6} ]];then
|
||||
cat ${blacklist} | awk -v ipv4addr="$piholeIP" -v ipv6addr="$piholeIPv6" '{sub(/\r$/,""); print ipv4addr" "$0"\n"ipv6addr" "$0}' >> ${adList}
|
||||
else
|
||||
cat ${blacklist} | awk -v ipv4addr="$piholeIP" '{sub(/\r$/,""); print ipv4addr" "$0}' >>${adList}
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo ":::"
|
||||
for dom in "${domToRemoveList[@]}"
|
||||
do
|
||||
#we need to remove the domains from the blacklist file and the host file
|
||||
echo "::: $dom"
|
||||
echo -n "::: removing from HOSTS file..."
|
||||
echo "$dom" | sed 's/\./\\./g' | xargs -I {} perl -i -ne'print unless /[^.]'{}'(?!.)/;' ${adList}
|
||||
echo " done!"
|
||||
echo -n "::: removing from blackist.txt..."
|
||||
echo "$dom" | sed 's/\./\\./g' | xargs -I {} perl -i -ne'print unless /'{}'(?!.)/;' ${blacklist}
|
||||
echo " done!"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
Reload() {
|
||||
# Reload hosts file
|
||||
echo ":::"
|
||||
echo -n "::: Refresh lists in dnsmasq..."
|
||||
|
||||
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
|
||||
else
|
||||
# service not running, start it up
|
||||
if [ -x "$(command -v systemctl)" ]; then
|
||||
systemctl start dnsmasq
|
||||
else
|
||||
service dnsmasq start
|
||||
fi
|
||||
fi
|
||||
echo " done!"
|
||||
}
|
||||
|
||||
DisplayBlist() {
|
||||
verbose=false
|
||||
echo -e " Displaying Gravity Affected Domains \n"
|
||||
count=1
|
||||
while IFS= read -r AD
|
||||
do
|
||||
echo "${count}: $AD"
|
||||
count=$((count+1))
|
||||
done < "$blacklist"
|
||||
}
|
||||
|
||||
###################################################
|
||||
|
||||
for var in "$@"
|
||||
do
|
||||
case "$var" in
|
||||
"-nr"| "--noreload" ) reload=false;;
|
||||
"-d" | "--delmode" ) addmode=false;;
|
||||
"-f" | "--force" ) force=true;;
|
||||
"-q" | "--quiet" ) verbose=false;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-l" | "--list" ) DisplayBlist;;
|
||||
* ) HandleOther "$var";;
|
||||
esac
|
||||
done
|
||||
|
||||
PopBlacklistFile
|
||||
|
||||
if ${modifyHost} || ${force}; then
|
||||
ModifyHostFile
|
||||
else
|
||||
if ${verbose}; then
|
||||
echo "::: No changes need to be made"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ${reload}; then
|
||||
Reload
|
||||
fi
|
@@ -1,148 +1,573 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1090,SC1091
|
||||
# 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
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Calculates stats and displays to an LCD
|
||||
#
|
||||
# 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.
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
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/127.0.0.1/$ftl_port"
|
||||
|
||||
#Functions##############################################################################################################
|
||||
piLog="/var/log/pihole.log"
|
||||
gravity="/etc/pihole/gravity.list"
|
||||
# Test if connection is open
|
||||
if { "true" >&3; } 2> /dev/null; then
|
||||
# Send command to FTL
|
||||
echo -e ">$1" >&3
|
||||
|
||||
today=$(date "+%b %e")
|
||||
# Read input
|
||||
read -r -t 1 LINE <&3
|
||||
until [[ ! $? ]] || [[ "$LINE" == *"EOM"* ]]; do
|
||||
echo "$LINE" >&1
|
||||
read -r -t 1 LINE <&3
|
||||
done
|
||||
|
||||
CalcBlockedDomains(){
|
||||
CheckIPv6
|
||||
if [ -e "$gravity" ]; then
|
||||
#Are we IPV6 or IPV4?
|
||||
if [[ -n ${piholeIPv6} ]];then
|
||||
#We are IPV6
|
||||
blockedDomainsTotal=$(wc -l /etc/pihole/gravity.list | awk '{print $1/2}')
|
||||
else
|
||||
#We are IPV4
|
||||
blockedDomainsTotal=$(wc -l /etc/pihole/gravity.list | awk '{print $1}')
|
||||
fi
|
||||
else
|
||||
blockedDomainsTotal="Err."
|
||||
fi
|
||||
# Close connection
|
||||
exec 3>&-
|
||||
exec 3<&-
|
||||
fi
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
CalcQueriesToday(){
|
||||
if [ -e "$piLog" ];then
|
||||
queriesToday=$(cat "$piLog" | grep "$today" | awk '/query/ {print $6}' | wc -l)
|
||||
else
|
||||
queriesToday="Err."
|
||||
fi
|
||||
# Print spaces to align right-side additional text
|
||||
printFunc() {
|
||||
local text_last
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
CalcblockedToday(){
|
||||
if [ -e "$piLog" ] && [ -e "$gravity" ];then
|
||||
blockedToday=$(cat ${piLog} | awk '/\/etc\/pihole\/gravity.list/ && !/address/ {print $6}' | wc -l)
|
||||
else
|
||||
blockedToday="Err."
|
||||
fi
|
||||
# Perform on first Chrono run (not for JSON formatted string)
|
||||
get_init_stats() {
|
||||
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 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
|
||||
|
||||
# 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
|
||||
|
||||
sys_throttle_raw=$(vgt=$(sudo vcgencmd get_throttled); echo "${vgt##*x}")
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
*) 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 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
|
||||
}
|
||||
|
||||
CalcPercentBlockedToday(){
|
||||
if [ "$queriesToday" != "Err." ] && [ "$blockedToday" != "Err." ]; then
|
||||
if [ "$queriesToday" != 0 ]; then #Fixes divide by zero error :)
|
||||
#scale 2 rounds the number down, so we'll do scale 4 and then trim the last 2 zeros
|
||||
percentBlockedToday=$(echo "scale=4; $blockedToday/$queriesToday*100" | bc)
|
||||
percentBlockedToday=$(sed 's/.\{2\}$//' <<< "$percentBlockedToday")
|
||||
else
|
||||
percentBlockedToday=0
|
||||
fi
|
||||
fi
|
||||
get_sys_stats() {
|
||||
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
|
||||
# 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=$(route -n | awk '$4 == "UG" {print $2;exit}')
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
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
|
||||
}
|
||||
|
||||
CheckIPv6(){
|
||||
piholeIPv6file="/etc/pihole/.useIPv6"
|
||||
if [[ -f ${piholeIPv6file} ]];then
|
||||
# If the file exists, then the user previously chose to use IPv6 in the automated installer
|
||||
piholeIPv6=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }')
|
||||
fi
|
||||
get_ftl_stats() {
|
||||
local stats_raw
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
outputJSON(){
|
||||
CalcQueriesToday
|
||||
CalcblockedToday
|
||||
CalcPercentBlockedToday
|
||||
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"
|
||||
|
||||
CalcBlockedDomains
|
||||
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
|
||||
|
||||
printf '{"domains_being_blocked":"%s","dns_queries_today":"%s","ads_blocked_today":"%s","ads_percentage_today":"%s"}\n' "$blockedDomainsTotal" "$queriesToday" "$blockedToday" "$percentBlockedToday"
|
||||
[[ "$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}"
|
||||
}
|
||||
|
||||
normalChrono(){
|
||||
for (( ; ; ))
|
||||
do
|
||||
clear
|
||||
# Displays a colorful Pi-hole logo
|
||||
echo " [0;1;35;95m_[0;1;31;91m__[0m [0;1;33;93m_[0m [0;1;34;94m_[0m [0;1;36;96m_[0m"
|
||||
echo "[0;1;31;91m|[0m [0;1;33;93m_[0m [0;1;32;92m(_[0;1;36;96m)_[0;1;34;94m__[0;1;35;95m|[0m [0;1;31;91m|_[0m [0;1;32;92m__[0;1;36;96m_|[0m [0;1;34;94m|[0;1;35;95m__[0;1;31;91m_[0m"
|
||||
echo "[0;1;33;93m|[0m [0;1;32;92m_[0;1;36;96m/[0m [0;1;34;94m|_[0;1;35;95m__[0;1;31;91m|[0m [0;1;33;93m'[0m [0;1;32;92m\/[0m [0;1;36;96m_[0m [0;1;34;94m\[0m [0;1;35;95m/[0m [0;1;31;91m-[0;1;33;93m_)[0m"
|
||||
echo "[0;1;32;92m|_[0;1;36;96m|[0m [0;1;34;94m|_[0;1;35;95m|[0m [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"
|
||||
echo ""
|
||||
echo " $(ifconfig eth0 | awk '/inet addr/ {print $2}' | cut -d':' -f2)"
|
||||
echo ""
|
||||
uptime | cut -d' ' -f11-
|
||||
#uptime -p #Doesn't work on all versions of uptime
|
||||
uptime | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/) {d=$6;h=$8;m=$9} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes."}'
|
||||
echo "-------------------------------"
|
||||
# Uncomment to continually read the log file and display the current domain being blocked
|
||||
#tail -f /var/log/pihole.log | awk '/\/etc\/pihole\/gravity.list/ {if ($7 != "address" && $7 != "name" && $7 != "/etc/pihole/gravity.list") print $7; else;}'
|
||||
chronoFunc() {
|
||||
get_init_stats
|
||||
|
||||
#uncomment next 4 lines to use original query count calculation
|
||||
#today=$(date "+%b %e")
|
||||
#todaysQueryCount=$(cat /var/log/pihole.log | grep "$today" | awk '/query/ {print $7}' | wc -l)
|
||||
#todaysQueryCountV4=$(cat /var/log/pihole.log | grep "$today" | awk '/query/ && /\[A\]/ {print $7}' | wc -l)
|
||||
#todaysQueryCountV6=$(cat /var/log/pihole.log | grep "$today" | awk '/query/ && /\[AAAA\]/ {print $7}' | wc -l)
|
||||
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
|
||||
|
||||
CalcQueriesToday
|
||||
CalcblockedToday
|
||||
CalcPercentBlockedToday
|
||||
# Get refresh number
|
||||
if [[ "$*" == *"-r"* ]]; then
|
||||
num="$*"
|
||||
num="${num/*-r /}"
|
||||
num="${num/ */}"
|
||||
num_str="Refresh set for every $num seconds"
|
||||
else
|
||||
num_str=""
|
||||
fi
|
||||
|
||||
CalcBlockedDomains
|
||||
clear
|
||||
|
||||
echo "Blocking: $blockedDomainsTotal"
|
||||
#below commented line does not add up to todaysQueryCount
|
||||
#echo "Queries: $todaysQueryCountV4 / $todaysQueryCountV6"
|
||||
echo "Queries: $queriesToday" #same total calculation as dashboard
|
||||
echo "Pi-holed: $blockedToday ($percentBlockedToday%)"
|
||||
# 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
|
||||
[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
|
||||
[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
|
||||
${COL_DARK_GRAY}$scr_line_str${COL_NC}"
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
done
|
||||
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
|
||||
}
|
||||
|
||||
displayHelp(){
|
||||
echo "::: Displays stats about your piHole!"
|
||||
echo ":::"
|
||||
echo "::: Usage: sudo pihole -c [optional:-j]"
|
||||
echo "::: Note: If no option is passed, then stats are displayed on screen, updated every 5 seconds"
|
||||
echo ":::"
|
||||
echo "::: Options:"
|
||||
echo "::: -j, --json output stats as JSON formatted string"
|
||||
echo "::: -h, --help display this help text"
|
||||
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}}"
|
||||
}
|
||||
|
||||
exit 1
|
||||
helpFunc() {
|
||||
if [[ "$1" == "?" ]]; then
|
||||
echo "Unknown option. Please view 'pihole -c --help' for more information"
|
||||
else
|
||||
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
|
||||
normalChrono
|
||||
chronoFunc
|
||||
fi
|
||||
|
||||
for var in "$@"
|
||||
do
|
||||
for var in "$@"; do
|
||||
case "$var" in
|
||||
"-j" | "--json" ) outputJSON;;
|
||||
"-h" | "--help" ) displayHelp;;
|
||||
* ) exit 1;;
|
||||
"-j" | "--json" ) jsonFunc;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-r" | "--refresh" ) chronoFunc "$@";;
|
||||
"-e" | "--exit" ) chronoFunc "$@";;
|
||||
* ) helpFunc "?";;
|
||||
esac
|
||||
done
|
||||
|
261
advanced/Scripts/list.sh
Executable file
261
advanced/Scripts/list.sh
Executable file
@@ -0,0 +1,261 @@
|
||||
#!/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.
|
||||
#
|
||||
# Whitelist and blacklist domains
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
# Globals
|
||||
basename=pihole
|
||||
piholeDir=/etc/${basename}
|
||||
whitelist=${piholeDir}/whitelist.txt
|
||||
blacklist=${piholeDir}/blacklist.txt
|
||||
readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
|
||||
reload=false
|
||||
addmode=true
|
||||
verbose=true
|
||||
|
||||
domList=()
|
||||
|
||||
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
|
||||
|
||||
echo "Usage: pihole -${param} [options] <domain> <domain2 ...>
|
||||
Example: 'pihole -${param} site.com', or 'pihole -${param} site1.com site2.com'
|
||||
${type^}list one or more domains
|
||||
|
||||
Options:
|
||||
-d, --delmode Remove domain(s) from the ${type}list
|
||||
-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
|
||||
--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"
|
||||
}
|
||||
|
||||
HandleOther() {
|
||||
# Convert to lowercase
|
||||
domain="${1,,}"
|
||||
|
||||
# Check validity of domain
|
||||
if [[ "${#domain}" -le 253 ]]; then
|
||||
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
|
||||
|
||||
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}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
AddDomain() {
|
||||
list="$2"
|
||||
domain=$(EscapeRegexp "$1")
|
||||
|
||||
[[ "${list}" == "${whitelist}" ]] && listname="whitelist"
|
||||
[[ "${list}" == "${blacklist}" ]] && listname="blacklist"
|
||||
[[ "${list}" == "${wildcardlist}" ]] && listname="wildcard blacklist"
|
||||
|
||||
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}" == "${wildcardlist}" ]]; then
|
||||
source "${piholeDir}/setupVars.conf"
|
||||
# Remove the /* from the end of the IP addresses
|
||||
IPV4_ADDRESS=${IPV4_ADDRESS%/*}
|
||||
IPV6_ADDRESS=${IPV6_ADDRESS%/*}
|
||||
[[ -z "${type}" ]] && type="--wildcard-only"
|
||||
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 -e " ${INFO} Adding $1 to wildcard blacklist..."
|
||||
fi
|
||||
reload="restart"
|
||||
echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}"
|
||||
if [[ "${#IPV6_ADDRESS}" > 0 ]]; then
|
||||
echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}"
|
||||
fi
|
||||
else
|
||||
if [[ "${verbose}" == true ]]; then
|
||||
echo -e " ${INFO} ${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"
|
||||
[[ "${list}" == "${wildcardlist}" ]] && listname="wildcard blacklist"
|
||||
|
||||
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
|
||||
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
|
||||
elif [[ "${list}" == "${wildcardlist}" ]]; then
|
||||
[[ -z "${type}" ]] && type="--wildcard-only"
|
||||
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 -e " ${INFO} Removing $1 from $listname..."
|
||||
# /I flag: search case-insensitive
|
||||
sed -i "/address=\/${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
|
||||
}
|
||||
|
||||
# Update Gravity
|
||||
Reload() {
|
||||
echo ""
|
||||
pihole -g --skip-download "${type:-}"
|
||||
}
|
||||
|
||||
Displaylist() {
|
||||
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; 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;;
|
||||
"-q" | "--quiet" ) verbose=false;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-l" | "--list" ) Displaylist;;
|
||||
"--nuke" ) NukeList;;
|
||||
* ) HandleOther "${var}";;
|
||||
esac
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
helpFunc
|
||||
fi
|
||||
|
||||
PoplistFile
|
||||
|
||||
if [[ "${reload}" != false ]]; then
|
||||
# Ensure that "restart" is used for Wildcard updates
|
||||
Reload "${reload}"
|
||||
fi
|
359
advanced/Scripts/piholeCheckout.sh
Normal file
359
advanced/Scripts/piholeCheckout.sh
Normal file
@@ -0,0 +1,359 @@
|
||||
#!/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.
|
||||
#
|
||||
# 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"
|
||||
|
||||
# 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
|
||||
|
||||
source "${setupVars}"
|
||||
update="false"
|
||||
|
||||
coltable="/opt/pihole/COL_TABLE"
|
||||
source ${coltable}
|
||||
|
||||
check_download_exists() {
|
||||
status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1)
|
||||
if grep -q "404" <<< "$status"; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
FTLinstall() {
|
||||
# Download and install FTL binary
|
||||
local binary
|
||||
binary="${1}"
|
||||
local path
|
||||
path="${2}"
|
||||
local str
|
||||
str="Installing FTL"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
|
||||
if curl -sSL --fail "https://ftl.pi-hole.net/${path}" -o "/tmp/${binary}"; then
|
||||
# Get sha1 of the binary we just downloaded for verification.
|
||||
curl -sSL --fail "https://ftl.pi-hole.net/${path}.sha1" -o "/tmp/${binary}.sha1"
|
||||
# Check if we just downloaded text, or a binary file.
|
||||
cd /tmp || return 1
|
||||
if sha1sum --status --quiet -c "${binary}".sha1; then
|
||||
echo -n "transferred... "
|
||||
stop_service pihole-FTL &> /dev/null
|
||||
install -T -m 0755 "/tmp/${binary}" "/usr/bin/pihole-FTL"
|
||||
rm "/tmp/${binary}" "/tmp/${binary}.sha1"
|
||||
start_service pihole-FTL &> /dev/null
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
return 0
|
||||
else
|
||||
echo -e "${OVER} ${CROSS} ${str}"
|
||||
echo -e " ${COL_LIGHT_RED}Error: Download of binary from ftl.pi-hole.net failed${COL_NC}"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo -e "${OVER} ${CROSS} ${str}"
|
||||
echo -e " ${COL_LIGHT_RED}Error: URL not found${COL_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
get_binary_name() {
|
||||
local machine
|
||||
machine=$(uname -m)
|
||||
|
||||
local str
|
||||
str="Detecting architecture"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
if [[ "${machine}" == "arm"* || "${machine}" == *"aarch"* ]]; then
|
||||
# ARM
|
||||
local rev
|
||||
rev=$(uname -m | sed "s/[^0-9]//g;")
|
||||
local lib
|
||||
lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }')
|
||||
if [[ "${lib}" == "/lib/ld-linux-aarch64.so.1" ]]; then
|
||||
echo -e "${OVER} ${TICK} Detected ARM-aarch64 architecture"
|
||||
binary="pihole-FTL-aarch64-linux-gnu"
|
||||
elif [[ "${lib}" == "/lib/ld-linux-armhf.so.3" ]]; then
|
||||
if [[ "$rev" -gt "6" ]]; then
|
||||
echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv7+)"
|
||||
binary="pihole-FTL-arm-linux-gnueabihf"
|
||||
else
|
||||
echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv6 or lower) Using ARM binary"
|
||||
binary="pihole-FTL-arm-linux-gnueabi"
|
||||
fi
|
||||
else
|
||||
echo -e "${OVER} ${TICK} Detected ARM architecture"
|
||||
binary="pihole-FTL-arm-linux-gnueabi"
|
||||
fi
|
||||
elif [[ "${machine}" == "ppc" ]]; then
|
||||
# PowerPC
|
||||
echo -e "${OVER} ${TICK} Detected PowerPC architecture"
|
||||
binary="pihole-FTL-powerpc-linux-gnu"
|
||||
elif [[ "${machine}" == "x86_64" ]]; then
|
||||
# 64bit
|
||||
echo -e "${OVER} ${TICK} Detected x86_64 architecture"
|
||||
binary="pihole-FTL-linux-x86_64"
|
||||
else
|
||||
# Something else - we try to use 32bit executable and warn the user
|
||||
if [[ ! "${machine}" == "i686" ]]; then
|
||||
echo -e "${OVER} ${CROSS} ${str}...
|
||||
${COL_LIGHT_RED}Not able to detect architecture (unknown: ${machine}), trying 32bit executable
|
||||
Contact support if you experience issues (e.g: FTL not running)${COL_NC}"
|
||||
else
|
||||
echo -e "${OVER} ${TICK} Detected 32bit (i686) architecture"
|
||||
fi
|
||||
binary="pihole-FTL-linux-x86_32"
|
||||
fi
|
||||
}
|
||||
|
||||
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
|
||||
directory="${1}"
|
||||
local output
|
||||
|
||||
cd "${directory}" || return 1
|
||||
# Get reachable remote branches, but store STDERR as STDOUT variable
|
||||
output=$( { git remote show origin | grep 'tracked' | sed 's/tracked//;s/ //g'; } 2>&1 )
|
||||
echo "$output"
|
||||
return
|
||||
}
|
||||
|
||||
fetch_checkout_pull_branch() {
|
||||
# Check out specified branch
|
||||
local directory
|
||||
directory="${1}"
|
||||
local branch
|
||||
branch="${2}"
|
||||
|
||||
# Set the reference for the requested branch, fetch, check it put and pull it
|
||||
cd "${directory}" || return 1
|
||||
git remote set-branches origin "${branch}" || return 1
|
||||
git stash --all --quiet &> /dev/null || true
|
||||
git clean --quiet --force -d || true
|
||||
git fetch --quiet || return 1
|
||||
checkout_pull_branch "${directory}" "${branch}" || return 1
|
||||
}
|
||||
|
||||
checkout_pull_branch() {
|
||||
# Check out specified branch
|
||||
local directory
|
||||
directory="${1}"
|
||||
local branch
|
||||
branch="${2}"
|
||||
local oldbranch
|
||||
|
||||
cd "${directory}" || return 1
|
||||
|
||||
oldbranch="$(git symbolic-ref HEAD)"
|
||||
|
||||
str="Switching to branch: '${branch}' from '${oldbranch}'"
|
||||
echo -ne " ${INFO} $str"
|
||||
git checkout "${branch}" --quiet || return 1
|
||||
echo -e "${OVER} ${TICK} $str"
|
||||
|
||||
|
||||
if [[ "$(git diff "${oldbranch}" | grep -c "^")" -gt "0" ]]; then
|
||||
update="true"
|
||||
fi
|
||||
|
||||
git_pull=$(git pull || return 1)
|
||||
|
||||
if [[ "$git_pull" == *"up-to-date"* ]]; then
|
||||
echo -e " ${INFO} ${git_pull}"
|
||||
else
|
||||
echo -e "$git_pull\\n"
|
||||
fi
|
||||
|
||||
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 " ${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
|
||||
|
||||
# Avoid globbing
|
||||
set -f
|
||||
|
||||
# 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!
|
||||
Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
if [[ "${INSTALL_WEB}" == "true" ]]; then
|
||||
if ! is_repo "${webInterfaceDir}" ; then
|
||||
echo -e " ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!
|
||||
Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "${1}" ]]; then
|
||||
echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC}
|
||||
Try 'pihole checkout --help' for more information."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! warning1 ; then
|
||||
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}" == "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}"
|
||||
FTLinstall "${binary}" "${path}"
|
||||
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} == "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}"
|
||||
FTLinstall "${binary}" "${path}"
|
||||
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
|
||||
${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}" == "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
|
||||
${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"
|
||||
FTLinstall "${binary}" "${path}"
|
||||
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
|
||||
|
||||
else
|
||||
echo -e " ${INFO} Requested option \"${1}\" is not available"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Force updating everything
|
||||
if [[ ( ! "${1}" == "web" && ! "${1}" == "ftl" ) && "${update}" == "true" ]]; 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
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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
|
||||
# Flushes /var/log/pihole.log
|
||||
# (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.
|
||||
# Flushes Pi-hole's log file
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
echo -n "::: Flushing /var/log/pihole.log ..."
|
||||
echo " " > /var/log/pihole.log
|
||||
echo "... done!"
|
||||
colfile="/opt/pihole/COL_TABLE"
|
||||
source ${colfile}
|
||||
|
||||
if [[ "$@" != *"quiet"* ]]; then
|
||||
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
|
||||
fi
|
||||
|
||||
if [[ "$@" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Flushed /var/log/pihole.log"
|
||||
fi
|
||||
|
@@ -1,42 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2015 by Jacob Salmela
|
||||
# Network-wide ad blocking via your Raspberry Pi
|
||||
# http://pi-hole.net
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Automatically configures the Pi to use the 2.8 LCD screen to display stats on it (also works over ssh)
|
||||
#
|
||||
# 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.
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
|
||||
|
||||
############ FUNCTIONS ###########
|
||||
|
||||
# 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 ###########
|
||||
|
228
advanced/Scripts/update.sh
Executable file
228
advanced/Scripts/update.sh
Executable file
@@ -0,0 +1,228 @@
|
||||
#!/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.
|
||||
#
|
||||
# Check Pi-hole core and admin pages versions and determine what
|
||||
# upgrade (if any) is required. Automatically updates and reinstalls
|
||||
# application if update is detected.
|
||||
#
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
GitCheckUpdateAvail() {
|
||||
local directory="${1}"
|
||||
curdir=$PWD
|
||||
cd "${directory}" || return
|
||||
|
||||
# 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}")"
|
||||
|
||||
# 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 -e "\\n ${COL_LIGHT_RED}Error: Local revision could not be obtained, please contact Pi-hole Support
|
||||
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
|
||||
Additional debugging output:${COL_NC}"
|
||||
git status
|
||||
exit
|
||||
fi
|
||||
|
||||
# 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
|
||||
FTLversion=$(/usr/bin/pihole-FTL tag)
|
||||
local FTLlatesttag
|
||||
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
|
||||
}
|
||||
|
||||
main() {
|
||||
local pihole_version_current
|
||||
local web_version_current
|
||||
local basicError="\\n ${COL_LIGHT_RED}Unable to complete update, please contact Pi-hole Support${COL_NC}"
|
||||
|
||||
# shellcheck disable=1090,2154
|
||||
source "${setupVars}"
|
||||
|
||||
# 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!
|
||||
Please re-run install script from https://pi-hole.net${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
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
|
||||
core_update=false
|
||||
echo -e " ${INFO} Pi-hole Core:\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
|
||||
fi
|
||||
|
||||
if FTLcheckUpdate ; then
|
||||
FTL_update=true
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_YELLOW}update available${COL_NC}"
|
||||
else
|
||||
FTL_update=false
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
|
||||
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 -e " ${INFO} FTL out of date"
|
||||
FTLdetect
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ "${INSTALL_WEB}" == true ]]; then
|
||||
if ! is_repo "${ADMIN_INTERFACE_DIR}" ; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!
|
||||
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 -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
|
||||
|
||||
# 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 ! ${core_update} && ! ${web_update} ; then
|
||||
if ! ${FTL_update} ; then
|
||||
echo ""
|
||||
echo -e " ${TICK} Everything is up to date!"
|
||||
exit 0
|
||||
fi
|
||||
elif ! ${core_update} && ${web_update} ; then
|
||||
echo ""
|
||||
echo -e " ${INFO} Pi-hole Web Admin files out of date"
|
||||
getGitFiles "${ADMIN_INTERFACE_DIR}" "${ADMIN_INTERFACE_GIT_URL}"
|
||||
elif ${core_update} && ! ${web_update} ; then
|
||||
echo ""
|
||||
echo -e " ${INFO} 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 -e "${basicError}" && exit 1
|
||||
elif ${core_update} && ${web_update} ; then
|
||||
echo ""
|
||||
echo -e " ${INFO} 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 -e "${basicError}" && exit 1
|
||||
else
|
||||
echo -e " ${COL_LIGHT_RED}Update script has malfunctioned, please contact Pi-hole Support${COL_NC}"
|
||||
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 -e " ${INFO} Everything is up to date!"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
echo -e " ${INFO} 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 -e "${basicError}" && exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${web_update}" == true ]]; then
|
||||
web_version_current="$(/usr/local/bin/pihole version --admin --current)"
|
||||
echo ""
|
||||
echo -e " ${INFO} Web Admin version is now at ${web_version_current/* v/v}
|
||||
${INFO} If you had made any changes in '/var/www/html/admin/', 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 -e " ${INFO} Pi-hole version is now at ${pihole_version_current/* v/v}
|
||||
${INFO} If you had made any changes in '/etc/.pihole/', they have been stashed using 'git stash'"
|
||||
fi
|
||||
|
||||
if [[ "${FTL_update}" == true ]]; then
|
||||
FTL_version_current="$(/usr/bin/pihole-FTL tag)"
|
||||
echo -e "\\n ${INFO} FTL version is now at ${FTL_version_current/* v/v}"
|
||||
start_service pihole-FTL
|
||||
enable_service pihole-FTL
|
||||
fi
|
||||
|
||||
echo ""
|
||||
exit 0
|
||||
}
|
||||
|
||||
main
|
66
advanced/Scripts/updatecheck.sh
Executable file
66
advanced/Scripts/updatecheck.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/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
|
||||
}
|
||||
|
||||
if [[ "$2" == "remote" ]]; then
|
||||
|
||||
if [[ "$3" == "reboot" ]]; then
|
||||
sleep 30
|
||||
fi
|
||||
|
||||
GITHUB_CORE_VERSION="$(json_extract tag_name "$(curl -q 'https://api.github.com/repos/pi-hole/pi-hole/releases/latest' 2> /dev/null)")"
|
||||
GITHUB_WEB_VERSION="$(json_extract tag_name "$(curl -q 'https://api.github.com/repos/pi-hole/AdminLTE/releases/latest' 2> /dev/null)")"
|
||||
GITHUB_FTL_VERSION="$(json_extract tag_name "$(curl -q 'https://api.github.com/repos/pi-hole/FTL/releases/latest' 2> /dev/null)")"
|
||||
|
||||
echo -n "${GITHUB_CORE_VERSION} ${GITHUB_WEB_VERSION} ${GITHUB_FTL_VERSION}" > "/etc/pihole/GitHubVersions"
|
||||
|
||||
else
|
||||
|
||||
CORE_BRANCH="$(get_local_branch /etc/.pihole)"
|
||||
WEB_BRANCH="$(get_local_branch /var/www/html/admin)"
|
||||
FTL_BRANCH="$(pihole-FTL branch)"
|
||||
|
||||
echo -n "${CORE_BRANCH} ${WEB_BRANCH} ${FTL_BRANCH}" > "/etc/pihole/localbranches"
|
||||
|
||||
CORE_VERSION="$(get_local_version /etc/.pihole)"
|
||||
WEB_VERSION="$(get_local_version /var/www/html/admin)"
|
||||
FTL_VERSION="$(pihole-FTL version)"
|
||||
|
||||
echo -n "${CORE_VERSION} ${WEB_VERSION} ${FTL_VERSION}" > "/etc/pihole/localversions"
|
||||
|
||||
fi
|
176
advanced/Scripts/version.sh
Normal file → Executable file
176
advanced/Scripts/version.sh
Normal file → Executable file
@@ -1,20 +1,168 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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
|
||||
# Whitelists domains
|
||||
# (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.
|
||||
# Show version numbers
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
piholeVersion=$(cd /etc/.pihole/ && git describe --tags --abbrev=0)
|
||||
webVersion=$(cd /var/www/html/admin/ && git describe --tags --abbrev=0)
|
||||
# Variables
|
||||
DEFAULT="-1"
|
||||
COREGITDIR="/etc/.pihole/"
|
||||
WEBGITDIR="/var/www/html/admin/"
|
||||
|
||||
piholeVersionLatest=$(curl -s https://api.github.com/repos/pi-hole/pi-hole/releases/latest | grep -Po '"tag_name":.*?[^\\]",' | perl -pe 's/"tag_name": "//; s/^"//; s/",$//')
|
||||
webVersionLatest=$(curl -s https://api.github.com/repos/pi-hole/AdminLTE/releases/latest | grep -Po '"tag_name":.*?[^\\]",' | perl -pe 's/"tag_name": "//; s/^"//; s/",$//')
|
||||
getLocalVersion() {
|
||||
# FTL requires a different method
|
||||
if [[ "$1" == "FTL" ]]; then
|
||||
pihole-FTL version
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "::: Pi-hole version is $piholeVersion (Latest version is $piholeVersionLatest)"
|
||||
echo "::: Web-Admin version is $webVersion (Latest version is $webVersionLatest)"
|
||||
# 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
|
||||
|
||||
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"
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
defaultOutput() {
|
||||
versionOutput "pi-hole" "$@"
|
||||
versionOutput "AdminLTE" "$@"
|
||||
versionOutput "FTL" "$@"
|
||||
}
|
||||
|
||||
helpFunc() {
|
||||
echo "Usage: pihole -v [repo | option] [option]
|
||||
Example: 'pihole -v -p -l'
|
||||
Show Pi-hole, Admin Console & FTL versions
|
||||
|
||||
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
|
||||
--hash Return the Github hash from your local repositories
|
||||
-h, --help Show this help dialog"
|
||||
exit 0
|
||||
}
|
||||
|
||||
case "${1}" in
|
||||
"-p" | "--pihole" ) shift; versionOutput "pi-hole" "$@";;
|
||||
"-a" | "--admin" ) shift; versionOutput "AdminLTE" "$@";;
|
||||
"-f" | "--ftl" ) shift; versionOutput "FTL" "$@";;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
* ) defaultOutput "$@";;
|
||||
esac
|
||||
|
530
advanced/Scripts/webpage.sh
Executable file
530
advanced/Scripts/webpage.sh
Executable file
@@ -0,0 +1,530 @@
|
||||
#!/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.
|
||||
#
|
||||
# Web interface settings
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
readonly setupVars="/etc/pihole/setupVars.conf"
|
||||
readonly dnsmasqconfig="/etc/dnsmasq.d/01-pihole.conf"
|
||||
readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.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]
|
||||
Example: pihole -a -p password
|
||||
Set options for the Admin Console
|
||||
|
||||
Options:
|
||||
-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
|
||||
}
|
||||
|
||||
add_setting() {
|
||||
echo "${1}=${2}" >> "${setupVars}"
|
||||
}
|
||||
|
||||
delete_setting() {
|
||||
sed -i "/${1}/d" "${setupVars}"
|
||||
}
|
||||
|
||||
change_setting() {
|
||||
delete_setting "${1}"
|
||||
add_setting "${1}" "${2}"
|
||||
}
|
||||
|
||||
add_dnsmasq_setting() {
|
||||
if [[ "${2}" != "" ]]; then
|
||||
echo "${1}=${2}" >> "${dnsmasqconfig}"
|
||||
else
|
||||
echo "${1}" >> "${dnsmasqconfig}"
|
||||
fi
|
||||
}
|
||||
|
||||
delete_dnsmasq_setting() {
|
||||
sed -i "/${1}/d" "${dnsmasqconfig}"
|
||||
}
|
||||
|
||||
SetTemperatureUnit() {
|
||||
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}
|
||||
}
|
||||
|
||||
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
|
||||
# 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 -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 -p "Confirm Password: " CONFIRM
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "${PASSWORD}" == "${CONFIRM}" ] ; then
|
||||
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}"
|
||||
|
||||
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
|
||||
|
||||
delete_dnsmasq_setting "domain-needed"
|
||||
|
||||
if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then
|
||||
add_dnsmasq_setting "domain-needed"
|
||||
fi
|
||||
|
||||
delete_dnsmasq_setting "bogus-priv"
|
||||
|
||||
if [[ "${DNS_BOGUS_PRIV}" == true ]]; then
|
||||
add_dnsmasq_setting "bogus-priv"
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
delete_dnsmasq_setting "host-record"
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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[5]}" == "dnssec" ]]; then
|
||||
change_setting "DNSSEC" "true"
|
||||
else
|
||||
change_setting "DNSSEC" "false"
|
||||
fi
|
||||
|
||||
ProcessDNSSettings
|
||||
|
||||
# Restart dnsmasq to load new configuration
|
||||
RestartDNS
|
||||
}
|
||||
|
||||
SetExcludeDomains() {
|
||||
change_setting "API_EXCLUDE_DOMAINS" "${args[2]}"
|
||||
}
|
||||
|
||||
SetExcludeClients() {
|
||||
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 &
|
||||
}
|
||||
|
||||
RestartDNS() {
|
||||
/usr/local/bin/pihole restartdns
|
||||
}
|
||||
|
||||
SetQueryLogOptions() {
|
||||
change_setting "API_QUERY_LOG_SHOW" "${args[2]}"
|
||||
}
|
||||
|
||||
ProcessDHCPSettings() {
|
||||
source "${setupVars}"
|
||||
|
||||
if [[ "${DHCP_ACTIVE}" == "true" ]]; then
|
||||
interface="${PIHOLE_INTERFACE}"
|
||||
|
||||
# Use eth0 as fallback interface
|
||||
if [ -z ${interface} ]; then
|
||||
interface="eth0"
|
||||
fi
|
||||
|
||||
if [[ "${PIHOLE_DOMAIN}" == "" ]]; then
|
||||
PIHOLE_DOMAIN="lan"
|
||||
change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}"
|
||||
fi
|
||||
|
||||
if [[ "${DHCP_LEASETIME}" == "0" ]]; then
|
||||
leasetime="infinite"
|
||||
elif [[ "${DHCP_LEASETIME}" == "" ]]; then
|
||||
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"
|
||||
fi
|
||||
|
||||
# Write settings to file
|
||||
echo "###############################################################################
|
||||
# DHCP SERVER CONFIG FILE AUTOMATICALLY POPULATED BY PI-HOLE WEB INTERFACE. #
|
||||
# ANY CHANGES MADE TO THIS FILE WILL BE LOST ON CHANGE #
|
||||
###############################################################################
|
||||
dhcp-authoritative
|
||||
dhcp-range=${DHCP_START},${DHCP_END},${leasetime}
|
||||
dhcp-option=option:router,${DHCP_ROUTER}
|
||||
dhcp-leasefile=/etc/pihole/dhcp.leases
|
||||
#quiet-dhcp
|
||||
" > "${dhcpconfig}"
|
||||
|
||||
if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then
|
||||
echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}"
|
||||
fi
|
||||
|
||||
if [[ "${DHCP_IPv6}" == "true" ]]; then
|
||||
echo "#quiet-dhcp6
|
||||
#enable-ra
|
||||
dhcp-option=option6:dns-server,[::]
|
||||
dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,${leasetime}
|
||||
ra-param=*,0,0
|
||||
" >> "${dhcpconfig}"
|
||||
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]}"
|
||||
|
||||
# Remove possible old setting from file
|
||||
delete_dnsmasq_setting "dhcp-"
|
||||
delete_dnsmasq_setting "quiet-dhcp"
|
||||
|
||||
ProcessDHCPSettings
|
||||
|
||||
RestartDNS
|
||||
}
|
||||
|
||||
DisableDHCP() {
|
||||
change_setting "DHCP_ACTIVE" "false"
|
||||
|
||||
# Remove possible old setting from file
|
||||
delete_dnsmasq_setting "dhcp-"
|
||||
delete_dnsmasq_setting "quiet-dhcp"
|
||||
|
||||
ProcessDHCPSettings
|
||||
|
||||
RestartDNS
|
||||
}
|
||||
|
||||
SetWebUILayout() {
|
||||
change_setting "WEBUIBOXEDLAYOUT" "${args[2]}"
|
||||
}
|
||||
|
||||
CustomizeAdLists() {
|
||||
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
|
||||
}
|
||||
|
||||
SetPrivacyMode() {
|
||||
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]}"
|
||||
|
||||
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]}"
|
||||
|
||||
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}"
|
||||
}
|
||||
|
||||
SetHostRecord() {
|
||||
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
|
||||
|
||||
Options:
|
||||
\"\" Empty: Remove host record
|
||||
-h, --help Show this help dialog"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
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]
|
||||
Example: 'pihole -a -i local'
|
||||
Specify dnsmasq's network interface listening behavior
|
||||
|
||||
Interfaces:
|
||||
local Listen on all interfaces, but only allow queries from
|
||||
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
|
||||
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"
|
||||
}
|
||||
|
||||
audit()
|
||||
{
|
||||
echo "${args[2]}" >> /etc/pihole/auditlog.list
|
||||
}
|
||||
|
||||
main() {
|
||||
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;;
|
||||
"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" ) audit;;
|
||||
* ) helpFunc;;
|
||||
esac
|
||||
|
||||
shift
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
helpFunc
|
||||
fi
|
||||
}
|
@@ -1,248 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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
|
||||
# Whitelists domains
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
helpFunc()
|
||||
{
|
||||
echo "::: Immediately whitelists one or more domains in the hosts file"
|
||||
echo ":::"
|
||||
echo "::: Usage: pihole -w domain1 [domain2 ...]"
|
||||
echo ":::"
|
||||
echo "::: Options:"
|
||||
echo "::: -d, --delmode Remove domains from the whitelist"
|
||||
echo "::: -nr, --noreload Update Whitelist without refreshing dnsmasq"
|
||||
echo "::: -f, --force Force updating of the hosts files, even if there are no changes"
|
||||
echo "::: -q, --quiet output is less verbose"
|
||||
echo "::: -h, --help Show this help dialog"
|
||||
echo "::: -l, --list Display your whitelisted domains"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
helpFunc
|
||||
fi
|
||||
|
||||
#globals
|
||||
basename=pihole
|
||||
piholeDir=/etc/${basename}
|
||||
adList=${piholeDir}/gravity.list
|
||||
whitelist=${piholeDir}/whitelist.txt
|
||||
reload=true
|
||||
addmode=true
|
||||
force=false
|
||||
verbose=true
|
||||
|
||||
domList=()
|
||||
domToRemoveList=()
|
||||
|
||||
piholeIPfile=/etc/pihole/piholeIP
|
||||
piholeIPv6file=/etc/pihole/.useIPv6
|
||||
|
||||
if [[ -f ${piholeIPfile} ]];then
|
||||
# If the file exists, it means it was exported from the installation script and we should use that value instead of detecting it in this script
|
||||
piholeIP=$(cat ${piholeIPfile})
|
||||
#rm $piholeIPfile
|
||||
else
|
||||
# Otherwise, the IP address can be taken directly from the machine, which will happen when the script is run by the user and not the installation script
|
||||
IPv4dev=$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)print $(i+1)}')
|
||||
piholeIPCIDR=$(ip -o -f inet addr show dev "$IPv4dev" | awk '{print $4}' | awk 'END {print}')
|
||||
piholeIP=${piholeIPCIDR%/*}
|
||||
fi
|
||||
|
||||
modifyHost=false
|
||||
|
||||
# After setting defaults, check if there's local overrides
|
||||
if [[ -r ${piholeDir}/pihole.conf ]];then
|
||||
echo "::: Local calibration requested..."
|
||||
. ${piholeDir}/pihole.conf
|
||||
fi
|
||||
|
||||
if [[ -f ${piholeIPv6file} ]];then
|
||||
# If the file exists, then the user previously chose to use IPv6 in the automated installer
|
||||
piholeIPv6=$(ip -6 route get 2001:4860:4860::8888 | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }')
|
||||
fi
|
||||
|
||||
HandleOther(){
|
||||
#check validity of domain
|
||||
validDomain=$(echo "$1" | perl -ne'print if /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/')
|
||||
if [ -z "$validDomain" ]; then
|
||||
echo "::: $1 is not a valid argument or domain name"
|
||||
else
|
||||
domList=("${domList[@]}" ${validDomain})
|
||||
fi
|
||||
}
|
||||
|
||||
PopWhitelistFile(){
|
||||
#check whitelist file exists, and if not, create it
|
||||
if [[ ! -f ${whitelist} ]];then
|
||||
touch ${whitelist}
|
||||
fi
|
||||
for dom in "${domList[@]}"
|
||||
do
|
||||
if ${addmode}; then
|
||||
AddDomain "$dom"
|
||||
else
|
||||
RemoveDomain "$dom"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
AddDomain(){
|
||||
#| sed 's/\./\\./g'
|
||||
bool=false
|
||||
|
||||
grep -Ex -q "$1" ${whitelist} || bool=true
|
||||
if ${bool}; then
|
||||
#domain not found in the whitelist file, add it!
|
||||
if ${verbose}; then
|
||||
echo -n "::: Adding $1 to $whitelist..."
|
||||
fi
|
||||
echo "$1" >> ${whitelist}
|
||||
modifyHost=true
|
||||
if ${verbose}; then
|
||||
echo " done!"
|
||||
fi
|
||||
else
|
||||
if ${verbose}; then
|
||||
echo "::: $1 already exists in $whitelist, no need to add!"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
RemoveDomain(){
|
||||
|
||||
bool=false
|
||||
grep -Ex -q "$1" ${whitelist} || bool=true
|
||||
if ${bool}; then
|
||||
#Domain is not in the whitelist file, no need to Remove
|
||||
if ${verbose}; then
|
||||
echo "::: $1 is NOT whitelisted! No need to remove"
|
||||
fi
|
||||
else
|
||||
#Domain is in the whitelist file, add to a temporary array and remove from whitelist file
|
||||
#if $verbose; then
|
||||
#echo "::: Un-whitelisting $dom..."
|
||||
#fi
|
||||
domToRemoveList=("${domToRemoveList[@]}" $1)
|
||||
modifyHost=true
|
||||
fi
|
||||
}
|
||||
|
||||
ModifyHostFile(){
|
||||
if ${addmode}; then
|
||||
#remove domains in from hosts file
|
||||
if [[ -r ${whitelist} ]];then
|
||||
# Remove whitelist entries
|
||||
numberOf=$(cat ${whitelist} | sed '/^\s*$/d' | wc -l)
|
||||
plural=; [[ "$numberOf" != "1" ]] && plural=s
|
||||
echo ":::"
|
||||
echo -n "::: Modifying HOSTS file to whitelist $numberOf domain${plural}..."
|
||||
awk -F':' '{print $1}' ${whitelist} | while read -r line; do echo "$piholeIP $line"; done > /etc/pihole/whitelist.tmp
|
||||
awk -F':' '{print $1}' ${whitelist} | while read -r line; do echo "$piholeIPv6 $line"; done >> /etc/pihole/whitelist.tmp
|
||||
echo "l" >> /etc/pihole/whitelist.tmp
|
||||
grep -F -x -v -f ${piholeDir}/whitelist.tmp ${adList} > ${piholeDir}/gravity.tmp
|
||||
rm ${adList}
|
||||
mv ${piholeDir}/gravity.tmp ${adList}
|
||||
rm ${piholeDir}/whitelist.tmp
|
||||
echo " done!"
|
||||
|
||||
fi
|
||||
else
|
||||
#we need to add the removed domains to the hosts file
|
||||
echo ":::"
|
||||
echo "::: Modifying HOSTS file to un-whitelist domains..."
|
||||
for rdom in "${domToRemoveList[@]}"
|
||||
do
|
||||
if grep -q "$rdom" /etc/pihole/*.domains; then
|
||||
echo "::: AdLists contain $rdom, re-adding block"
|
||||
if [[ -n ${piholeIPv6} ]];then
|
||||
echo -n "::: Restoring block for $rdom on IPv4 and IPv6..."
|
||||
echo "$rdom" | awk -v ipv4addr="$piholeIP" -v ipv6addr="$piholeIPv6" '{sub(/\r$/,""); print ipv4addr" "$0"\n"ipv6addr" "$0}' >> ${adList}
|
||||
echo " done!"
|
||||
else
|
||||
echo -n "::: Restoring block for $rdom on IPv4..."
|
||||
echo "$rdom" | awk -v ipv4addr="$piholeIP" '{sub(/\r$/,""); print ipv4addr" "$0}' >>${adList}
|
||||
echo " done!"
|
||||
fi
|
||||
fi
|
||||
echo -n "::: Removing $rdom from $whitelist..."
|
||||
echo "$rdom" | sed 's/\./\\./g' | xargs -I {} perl -i -ne'print unless /'{}'(?!.)/;' ${whitelist}
|
||||
echo " done!"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
Reload() {
|
||||
# Reload hosts file
|
||||
echo ":::"
|
||||
echo -n "::: Refresh lists in dnsmasq..."
|
||||
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
|
||||
else
|
||||
# service not running, start it up
|
||||
if [ -x "$(command -v systemctl)" ]; then
|
||||
systemctl start dnsmasq
|
||||
else
|
||||
service dnsmasq start
|
||||
fi
|
||||
fi
|
||||
echo " done!"
|
||||
}
|
||||
|
||||
DisplayWlist() {
|
||||
verbose=false
|
||||
echo -e " Displaying Gravity Resistant Domains \n"
|
||||
count=1
|
||||
while IFS= read -r RD
|
||||
do
|
||||
echo "${count}: $RD"
|
||||
count=$((count+1))
|
||||
done < "$whitelist"
|
||||
}
|
||||
|
||||
###################################################
|
||||
|
||||
for var in "$@"
|
||||
do
|
||||
case "$var" in
|
||||
"-nr"| "--noreload" ) reload=false;;
|
||||
"-d" | "--delmode" ) addmode=false;;
|
||||
"-f" | "--force" ) force=true;;
|
||||
"-q" | "--quiet" ) verbose=false;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-l" | "--list" ) DisplayWlist;;
|
||||
* ) HandleOther "$var";;
|
||||
esac
|
||||
done
|
||||
|
||||
PopWhitelistFile
|
||||
|
||||
if ${modifyHost} || ${force}; then
|
||||
ModifyHostFile
|
||||
else
|
||||
if ${verbose}; then
|
||||
echo ":::"
|
||||
echo "::: No changes need to be made"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ${reload}; then
|
||||
Reload
|
||||
fi
|
@@ -1,12 +1,11 @@
|
||||
_pihole()
|
||||
{
|
||||
local cur prev opts
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
opts="blacklist chronometer debug flush help query reconfigure setupLCD uninstall updateGravity updatePihole version whitelist"
|
||||
_pihole() {
|
||||
local cur prev opts
|
||||
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"
|
||||
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
}
|
||||
complete -F _pihole pihole
|
||||
complete -F _pihole pihole
|
||||
|
383
advanced/blockingpage.css
Normal file
383
advanced/blockingpage.css
Normal file
@@ -0,0 +1,383 @@
|
||||
/* 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. */
|
||||
|
||||
/* 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"; }
|
||||
|
||||
#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: #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 {
|
||||
background: #3c8dbc;
|
||||
display: table;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
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 */
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
background: #ecf0f5;
|
||||
font-size: 1.65rem;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#bpOutput.hidden { display: none; }
|
||||
#bpOutput.success { background: #00a65a; }
|
||||
#bpOutput.error { background: #dd4b39; }
|
||||
|
||||
.blockMsg, .flagMsg {
|
||||
font: bold 1.8rem Consolas, Courier, monospace;
|
||||
padding: 5px 10px 10px 10px;
|
||||
text-indent: 15px;
|
||||
}
|
||||
|
||||
#bpHelpTxt { padding-bottom: 10px; }
|
||||
|
||||
.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,12 +0,0 @@
|
||||
# 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
|
||||
# Swap file config
|
||||
#
|
||||
# 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.
|
||||
|
||||
CONF_SWAPSIZE=500
|
@@ -1,7 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<script>window.close();</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@@ -1 +0,0 @@
|
||||
var x = "Pi-hole: A black hole for Internet advertisements."
|
346
advanced/index.php
Normal file
346
advanced/index.php
Normal file
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
/* 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. */
|
||||
|
||||
// Sanitise HTTP_HOST output
|
||||
$serverName = htmlspecialchars($_SERVER["HTTP_HOST"]);
|
||||
|
||||
if (!is_file("/etc/pihole/setupVars.conf"))
|
||||
die("[ERROR] File not found: <code>/etc/pihole/setupVars.conf</code>");
|
||||
|
||||
// Get values from setupVars.conf
|
||||
$setupVars = parse_ini_file("/etc/pihole/setupVars.conf");
|
||||
$svPasswd = !empty($setupVars["WEBPASSWORD"]);
|
||||
$svEmail = (!empty($setupVars["ADMIN_EMAIL"]) && filter_var($setupVars["ADMIN_EMAIL"], FILTER_VALIDATE_EMAIL)) ? $setupVars["ADMIN_EMAIL"] : "";
|
||||
unset($setupVars);
|
||||
|
||||
// Set landing page location, found within /var/www/html/
|
||||
$landPage = "../landing.php";
|
||||
|
||||
// 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"]);
|
||||
}
|
||||
|
||||
// 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";
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
// 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</body></html>
|
||||
";
|
||||
|
||||
// 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 */
|
||||
|
||||
// Determine placeholder text based off $svPasswd presence
|
||||
$wlPlaceHolder = empty($svPasswd) ? "No admin password set" : "Javascript disabled";
|
||||
|
||||
// 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
|
||||
if (empty(glob("/etc/pihole/list.0.*.domains")))
|
||||
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">
|
||||
<?=$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 $svPasswd is present & JS is available
|
||||
if (!empty($svPasswd) && $featuredTotal > 0) {
|
||||
echo '$("#bpWLPassword, #bpWhitelist").prop("disabled", false);';
|
||||
echo '$("#bpWLPassword").attr("placeholder", "Password");';
|
||||
}
|
||||
?>
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body id="blockpage"><div id="bpWrapper">
|
||||
<header>
|
||||
<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 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="<?=$wlPlaceHolder ?>" disabled/><button id="bpWhitelist" type="button" disabled></button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<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 add() {
|
||||
$("#bpOutput").removeClass("hidden error exception");
|
||||
$("#bpOutput").addClass("add");
|
||||
var domain = "<?=$serverName ?>";
|
||||
var pw = $("#bpWLPassword");
|
||||
if(domain.length === 0) {
|
||||
return;
|
||||
}
|
||||
$.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");
|
||||
} else {
|
||||
$("#bpOutput").removeClass("add");
|
||||
$("#bpOutput").addClass("error");
|
||||
$("#bpOutput").html(""+response+"");
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
$("#bpOutput").removeClass("add");
|
||||
$("#bpOutput").addClass("exception");
|
||||
}
|
||||
});
|
||||
}
|
||||
<?php if ($featuredTotal > 0) { ?>
|
||||
$(document).keypress(function(e) {
|
||||
if(e.which === 13 && $("#bpWLPassword").is(":focus")) {
|
||||
add();
|
||||
}
|
||||
});
|
||||
$("#bpWhitelist").on("click", function() {
|
||||
add();
|
||||
});
|
||||
<?php } ?>
|
||||
</script>
|
||||
</body></html>
|
@@ -1,17 +1,24 @@
|
||||
# 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
|
||||
# lighttpd 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.
|
||||
# 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 SEPARATE CONFIG FILE: #
|
||||
# /etc/lighttpd/external.conf #
|
||||
###############################################################################
|
||||
|
||||
server.modules = (
|
||||
"mod_access",
|
||||
"mod_accesslog",
|
||||
"mod_auth",
|
||||
"mod_expire",
|
||||
"mod_compress",
|
||||
"mod_redirect",
|
||||
@@ -20,19 +27,18 @@ server.modules = (
|
||||
)
|
||||
|
||||
server.document-root = "/var/www/html"
|
||||
server.error-handler-404 = "pihole/index.html"
|
||||
server.error-handler-404 = "pihole/index.php"
|
||||
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
|
||||
server.errorlog = "/var/log/lighttpd/error.log"
|
||||
server.pid-file = "/var/run/lighttpd.pid"
|
||||
server.username = "www-data"
|
||||
server.groupname = "www-data"
|
||||
server.port = 80
|
||||
accesslog.filename = "/var/log/lighttpd/access.log"
|
||||
accesslog.format = "%{%s}t|%V|%r|%s|%b"
|
||||
|
||||
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/"
|
||||
@@ -41,21 +47,29 @@ 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"
|
||||
)
|
||||
|
||||
$HTTP["url"] =~ ".ttf$" {
|
||||
# Allow Block Page access to local fonts
|
||||
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
|
||||
}
|
||||
}
|
||||
|
||||
# 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." )
|
||||
# rewrite only js requests
|
||||
url.rewrite = ("(.*).js" => "pihole/index.js")
|
||||
# Block . files from being served, such as .git, .github, .gitignore
|
||||
$HTTP["url"] =~ "^/admin/\.(.*)" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
|
||||
# Add user chosen options held in external file
|
||||
include_shell "cat external.conf 2>/dev/null"
|
||||
|
@@ -1,16 +1,23 @@
|
||||
# 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
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# lighttpd config for Pi-hole
|
||||
#
|
||||
# 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.
|
||||
# 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 SEPARATE CONFIG FILE: #
|
||||
# /etc/lighttpd/external.conf #
|
||||
###############################################################################
|
||||
|
||||
server.modules = (
|
||||
"mod_access",
|
||||
"mod_auth",
|
||||
"mod_fastcgi",
|
||||
"mod_accesslog",
|
||||
"mod_expire",
|
||||
@@ -21,19 +28,19 @@ server.modules = (
|
||||
)
|
||||
|
||||
server.document-root = "/var/www/html"
|
||||
server.error-handler-404 = "pihole/index.html"
|
||||
server.error-handler-404 = "pihole/index.php"
|
||||
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
|
||||
server.errorlog = "/var/log/lighttpd/error.log"
|
||||
server.pid-file = "/var/run/lighttpd.pid"
|
||||
server.username = "lighttpd"
|
||||
server.groupname = "lighttpd"
|
||||
server.port = 80
|
||||
accesslog.filename = "/var/log/lighttpd/access.log"
|
||||
accesslog.format = "%{%s}t|%V|%r|%s|%b"
|
||||
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/"
|
||||
@@ -65,14 +72,22 @@ 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"
|
||||
)
|
||||
|
||||
$HTTP["url"] =~ ".ttf$" {
|
||||
# Allow Block Page access to local fonts
|
||||
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
|
||||
}
|
||||
}
|
||||
|
||||
# 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." )
|
||||
# rewrite only js requests
|
||||
url.rewrite = ("(.*).js" => "pihole/index.js")
|
||||
# Block . files from being served, such as .git, .github, .gitignore
|
||||
$HTTP["url"] =~ "^/admin/\.(.*)" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
|
||||
# Add user chosen options held in external file
|
||||
include_shell "cat external.conf 2>/dev/null"
|
||||
|
21
advanced/logrotate
Normal file
21
advanced/logrotate
Normal file
@@ -0,0 +1,21 @@
|
||||
/var/log/pihole.log {
|
||||
# su #
|
||||
daily
|
||||
copytruncate
|
||||
rotate 5
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
nomail
|
||||
}
|
||||
|
||||
/var/log/pihole-FTL.log {
|
||||
# su #
|
||||
weekly
|
||||
copytruncate
|
||||
rotate 3
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
nomail
|
||||
}
|
84
advanced/pihole-FTL.service
Normal file
84
advanced/pihole-FTL.service
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
# Provides: pihole-FTL
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: pihole-FTL daemon
|
||||
# Description: Enable service provided by pihole-FTL daemon
|
||||
### END INIT INFO
|
||||
|
||||
FTLUSER=pihole
|
||||
PIDFILE=/var/run/pihole-FTL.pid
|
||||
|
||||
get_pid() {
|
||||
pidof "pihole-FTL"
|
||||
}
|
||||
|
||||
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 /var/log/pihole.log
|
||||
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
|
||||
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 /var/log/pihole.log
|
||||
su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER"
|
||||
echo
|
||||
fi
|
||||
}
|
||||
|
||||
# Stop the service
|
||||
stop() {
|
||||
if is_running; then
|
||||
kill "$(get_pid)"
|
||||
for i in {1..5}; do
|
||||
if ! is_running; then
|
||||
break
|
||||
fi
|
||||
|
||||
echo -n "."
|
||||
sleep 1
|
||||
done
|
||||
echo
|
||||
|
||||
if is_running; then
|
||||
echo "Not stopped; may still be shutting down or shutdown may have failed, killing now"
|
||||
kill -9 "$(get_pid)"
|
||||
exit 1
|
||||
else
|
||||
echo "Stopped"
|
||||
fi
|
||||
else
|
||||
echo "Not running"
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
### main logic ###
|
||||
case "$1" in
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status pihole-FTL
|
||||
;;
|
||||
start|restart|reload|condrestart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|reload|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
@@ -1,25 +1,34 @@
|
||||
# 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
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Updates ad sources every week
|
||||
#
|
||||
# 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.
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
#
|
||||
#
|
||||
#
|
||||
# This file is under source-control of the Pi-hole installation and update
|
||||
# scripts, any changes made to this file will be overwritten when the softare
|
||||
# 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 /usr/local/bin/pihole updateGravity
|
||||
# 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
|
||||
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 /usr/local/bin/pihole updatePihole
|
||||
# Pi-hole: Flush the log daily at 00:00
|
||||
# The flush script will use logrotate if available
|
||||
# parameter "once": logrotate only once (default is twice)
|
||||
# parameter "quiet": don't print messages
|
||||
00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush once quiet
|
||||
|
||||
# Pi-hole: Parse the log file before it is flushed and save the stats to a database
|
||||
# This will be used for a historical view of your Pi-hole's performance
|
||||
#50 23 * * * root /usr/local/bin/dailyLog.sh # note: this is outdated
|
||||
@reboot root /usr/sbin/logrotate /etc/pihole/logrotate
|
||||
|
||||
# Pi-hole: Flush the log daily at 00:00 so it doesn't get out of control
|
||||
# Stats will be viewable in the Web interface thanks to the cron job above
|
||||
00 00 * * * root /usr/local/bin/pihole flush
|
||||
# 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,12 +1,9 @@
|
||||
# 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
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Allows the WebUI to use Pi-hole commands
|
||||
#
|
||||
# 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.
|
||||
|
||||
www-data ALL=NOPASSWD: /usr/local/bin/pihole
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
#
|
||||
|
@@ -1,87 +0,0 @@
|
||||
module pihole 1.0;
|
||||
|
||||
require {
|
||||
type var_log_t;
|
||||
type unconfined_t;
|
||||
type init_t;
|
||||
type auditd_t;
|
||||
type syslogd_t;
|
||||
type NetworkManager_t;
|
||||
type mdadm_t;
|
||||
type tuned_t;
|
||||
type avahi_t;
|
||||
type irqbalance_t;
|
||||
type system_dbusd_t;
|
||||
type kernel_t;
|
||||
type httpd_sys_script_t;
|
||||
type systemd_logind_t;
|
||||
type httpd_t;
|
||||
type policykit_t;
|
||||
type dnsmasq_t;
|
||||
type udev_t;
|
||||
type postfix_pickup_t;
|
||||
type sshd_t;
|
||||
type crond_t;
|
||||
type getty_t;
|
||||
type lvm_t;
|
||||
type postfix_qmgr_t;
|
||||
type postfix_master_t;
|
||||
class dir { getattr search };
|
||||
class file { read open setattr };
|
||||
}
|
||||
|
||||
#============= dnsmasq_t ==============
|
||||
allow dnsmasq_t var_log_t:file { open setattr };
|
||||
|
||||
#============= httpd_t ==============
|
||||
allow httpd_t var_log_t:file { read open };
|
||||
|
||||
#============= httpd_sys_script_t (class: dir) ==============
|
||||
allow httpd_sys_script_t NetworkManager_t:dir { getattr search };
|
||||
allow httpd_sys_script_t auditd_t:dir { getattr search };
|
||||
allow httpd_sys_script_t avahi_t:dir { getattr search };
|
||||
allow httpd_sys_script_t crond_t:dir { getattr search };
|
||||
allow httpd_sys_script_t dnsmasq_t:dir { getattr search };
|
||||
allow httpd_sys_script_t getty_t:dir { getattr search };
|
||||
allow httpd_sys_script_t httpd_t:dir { getattr search };
|
||||
allow httpd_sys_script_t init_t:dir { getattr search };
|
||||
allow httpd_sys_script_t irqbalance_t:dir { getattr search };
|
||||
allow httpd_sys_script_t kernel_t:dir { getattr search };
|
||||
allow httpd_sys_script_t lvm_t:dir { getattr search };
|
||||
allow httpd_sys_script_t mdadm_t:dir { getattr search };
|
||||
allow httpd_sys_script_t policykit_t:dir { getattr search };
|
||||
allow httpd_sys_script_t postfix_master_t:dir { getattr search };
|
||||
allow httpd_sys_script_t postfix_pickup_t:dir { getattr search };
|
||||
allow httpd_sys_script_t postfix_qmgr_t:dir { getattr search };
|
||||
allow httpd_sys_script_t sshd_t:dir { getattr search };
|
||||
allow httpd_sys_script_t syslogd_t:dir { getattr search };
|
||||
allow httpd_sys_script_t system_dbusd_t:dir { getattr search };
|
||||
allow httpd_sys_script_t systemd_logind_t:dir { getattr search };
|
||||
allow httpd_sys_script_t tuned_t:dir { getattr search };
|
||||
allow httpd_sys_script_t udev_t:dir { getattr search };
|
||||
allow httpd_sys_script_t unconfined_t:dir { getattr search };
|
||||
|
||||
#============= httpd_sys_script_t (class: file) ==============
|
||||
allow httpd_sys_script_t NetworkManager_t:file { read open };
|
||||
allow httpd_sys_script_t auditd_t:file { read open };
|
||||
allow httpd_sys_script_t avahi_t:file { read open };
|
||||
allow httpd_sys_script_t crond_t:file { read open };
|
||||
allow httpd_sys_script_t dnsmasq_t:file { read open };
|
||||
allow httpd_sys_script_t getty_t:file { read open };
|
||||
allow httpd_sys_script_t httpd_t:file { read open };
|
||||
allow httpd_sys_script_t init_t:file { read open };
|
||||
allow httpd_sys_script_t irqbalance_t:file { read open };
|
||||
allow httpd_sys_script_t kernel_t:file { read open };
|
||||
allow httpd_sys_script_t lvm_t:file { read open };
|
||||
allow httpd_sys_script_t mdadm_t:file { read open };
|
||||
allow httpd_sys_script_t policykit_t:file { read open };
|
||||
allow httpd_sys_script_t postfix_master_t:file { read open };
|
||||
allow httpd_sys_script_t postfix_pickup_t:file { read open };
|
||||
allow httpd_sys_script_t postfix_qmgr_t:file { read open };
|
||||
allow httpd_sys_script_t sshd_t:file { read open };
|
||||
allow httpd_sys_script_t syslogd_t:file { read open };
|
||||
allow httpd_sys_script_t system_dbusd_t:file { read open };
|
||||
allow httpd_sys_script_t systemd_logind_t:file { read open };
|
||||
allow httpd_sys_script_t tuned_t:file { read open };
|
||||
allow httpd_sys_script_t udev_t:file { read open };
|
||||
allow httpd_sys_script_t unconfined_t:file { read open };
|
File diff suppressed because it is too large
Load Diff
@@ -1,51 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Completely uninstalls Pi-hole
|
||||
#
|
||||
# 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.
|
||||
# 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 "\n ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;;
|
||||
* ) echo -e "\n ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Must be root to uninstall
|
||||
if [[ $EUID -eq 0 ]];then
|
||||
echo "::: You are root."
|
||||
str="Root user check"
|
||||
if [[ ${EUID} -eq 0 ]]; then
|
||||
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
|
||||
# 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 "::: Please install sudo or run this as root."
|
||||
exit 1
|
||||
fi
|
||||
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
|
||||
if [[ "${INSTALL_WEB}" == true ]]; then
|
||||
# Install the Web dependencies
|
||||
DEPS=("${INSTALLER_DEPS[@]}" "${PIHOLE_DEPS[@]}" "${PIHOLE_WEB_DEPS[@]}")
|
||||
# Otherwise,
|
||||
else
|
||||
# just install the Core dependencies
|
||||
DEPS=("${INSTALLER_DEPS[@]}" "${PIHOLE_DEPS[@]}")
|
||||
fi
|
||||
|
||||
# Compatability
|
||||
if [ -x "$(command -v rpm)" ];then
|
||||
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 )
|
||||
PKG_REMOVE="${PKG_MANAGER} remove -y"
|
||||
package_check() {
|
||||
rpm -qa | grep ^$1- > /dev/null
|
||||
}
|
||||
package_cleanup() {
|
||||
${SUDO} ${PKG_MANAGER} -y autoremove
|
||||
}
|
||||
elif [ -x "$(command -v apt-get)" ];then
|
||||
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 )
|
||||
PKG_REMOVE="${PKG_MANAGER} -y remove --purge"
|
||||
package_check() {
|
||||
dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -c "ok installed"
|
||||
}
|
||||
@@ -54,63 +76,51 @@ elif [ -x "$(command -v apt-get)" ];then
|
||||
${SUDO} ${PKG_MANAGER} -y autoclean
|
||||
}
|
||||
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"
|
||||
}
|
||||
|
||||
function removeAndPurge {
|
||||
removeAndPurge() {
|
||||
# Purge dependencies
|
||||
echo ":::"
|
||||
for i in "${PIHOLE_DEPS[@]}"; do
|
||||
echo ""
|
||||
for i in "${DEPS[@]}"; do
|
||||
package_check ${i} > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
if [[ "$?" -eq 0 ]]; then
|
||||
while true; do
|
||||
read -rp "::: Do you wish to remove $i from your system? [y/n]: " yn
|
||||
read -rp " ${QST} Do you wish to remove ${COL_WHITE}${i}${COL_NC} 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" "$i\n"; break;;
|
||||
* ) printf "::: You must answer yes or no!\n";;
|
||||
[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
|
||||
printf ":::\tPackage %s not installed... Not removing.\n" "$i"
|
||||
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/01-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";
|
||||
echo -ne " ${INFO} Removing & cleaning remaining dependencies..."
|
||||
package_cleanup &> /dev/null
|
||||
echo -e "${OVER} ${TICK} Removed & cleaned up remaining dependencies"
|
||||
|
||||
# Call removeNoPurge to remove PiHole specific files
|
||||
# Call removeNoPurge to remove Pi-hole specific files
|
||||
removeNoPurge
|
||||
}
|
||||
|
||||
function removeNoPurge {
|
||||
echo ":::"
|
||||
# Only web directories/files that are created by pihole should be removed.
|
||||
echo "::: Removing the Pi-hole Web server files..."
|
||||
removeNoPurge() {
|
||||
# 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 /var/www/html/index.lighttpd.orig &> /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
|
||||
@@ -118,59 +128,96 @@ function removeNoPurge {
|
||||
${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
|
||||
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
|
||||
echo -e " ${TICK} Restored the default system cron"
|
||||
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
|
||||
${SUDO} rm -f /etc/cron.d/pihole &> /dev/null
|
||||
echo -e " ${TICK} Removed /etc/cron.d/pihole"
|
||||
fi
|
||||
|
||||
echo "::: Removing config files and scripts..."
|
||||
package_check ${i} > /dev/null
|
||||
if [ $? -eq 1 ]; then
|
||||
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
|
||||
|
||||
${SUDO} rm /etc/dnsmasq.d/adList.conf &> /dev/null
|
||||
${SUDO} rm /etc/dnsmasq.d/01-pihole.conf &> /dev/null
|
||||
${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 /usr/local/bin/pihole &> /dev/null
|
||||
${SUDO} rm /etc/bash_completion.d/pihole &> /dev/null
|
||||
${SUDO} rm /etc/sudoers.d/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"
|
||||
|
||||
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 -L 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"
|
||||
# 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
|
||||
|
||||
# If the pihole user exists, then remove
|
||||
if id "pihole" &> /dev/null; then
|
||||
${SUDO} userdel -r pihole 2> /dev/null
|
||||
if [[ "$?" -eq 0 ]]; 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
|
||||
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
|
||||
|
914
gravity.sh
914
gravity.sh
@@ -1,359 +1,673 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1090
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2015 by Jacob Salmela
|
||||
# Network-wide ad blocking via your Raspberry Pi
|
||||
# http://pi-hole.net
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Usage: "pihole -g"
|
||||
# Compiles a list of ad-serving domains by downloading them from multiple sources
|
||||
#
|
||||
# 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.
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
# Run this script as root or under sudo
|
||||
echo ":::"
|
||||
coltable="/opt/pihole/COL_TABLE"
|
||||
source "${coltable}"
|
||||
|
||||
helpFunc()
|
||||
{
|
||||
echo "::: Pull in domains from adlists"
|
||||
echo ":::"
|
||||
echo "::: Usage: pihole -g"
|
||||
echo ":::"
|
||||
echo "::: Options:"
|
||||
echo "::: -f, --force Force lists to be downloaded, even if they don't need updating."
|
||||
echo "::: -h, --help Show this help dialog"
|
||||
exit 1
|
||||
}
|
||||
basename="pihole"
|
||||
PIHOLE_COMMAND="/usr/local/bin/${basename}"
|
||||
|
||||
piholeDir="/etc/${basename}"
|
||||
piholeRepo="/etc/.${basename}"
|
||||
|
||||
adListFile=/etc/pihole/adlists.list
|
||||
adListDefault=/etc/pihole/adlists.default
|
||||
whitelistScript=/opt/pihole/whitelist.sh
|
||||
blacklistScript=/opt/pihole/blacklist.sh
|
||||
adListFile="${piholeDir}/adlists.list"
|
||||
adListDefault="${piholeDir}/adlists.default"
|
||||
adListRepoDefault="${piholeRepo}/adlists.default"
|
||||
|
||||
#Source the setupVars from install script for the IP
|
||||
setupVars=/etc/pihole/setupVars.conf
|
||||
if [[ -f ${setupVars} ]];then
|
||||
. /etc/pihole/setupVars.conf
|
||||
else
|
||||
echo "::: WARNING: /etc/pihole/setupVars.conf missing. Possible installation failure."
|
||||
echo "::: Please run 'pihole -r', and choose the 'reconfigure' option to reconfigure."
|
||||
whitelistFile="${piholeDir}/whitelist.txt"
|
||||
blacklistFile="${piholeDir}/blacklist.txt"
|
||||
wildcardFile="/etc/dnsmasq.d/03-pihole-wildcard.conf"
|
||||
|
||||
adList="${piholeDir}/gravity.list"
|
||||
blackList="${piholeDir}/black.list"
|
||||
localList="${piholeDir}/local.list"
|
||||
VPNList="/etc/openvpn/ipp.txt"
|
||||
|
||||
domainsExtension="domains"
|
||||
matterAndLight="${basename}.0.matterandlight.txt"
|
||||
parsedMatter="${basename}.1.parsedmatter.txt"
|
||||
whitelistMatter="${basename}.2.whitelistmatter.txt"
|
||||
accretionDisc="${basename}.3.accretionDisc.txt"
|
||||
preEventHorizon="list.preEventHorizon"
|
||||
|
||||
skipDownload="false"
|
||||
|
||||
# Source setupVars from install script
|
||||
setupVars="${piholeDir}/setupVars.conf"
|
||||
if [[ -f "${setupVars}" ]];then
|
||||
source "${setupVars}"
|
||||
|
||||
# Remove CIDR mask from IPv4/6 addresses
|
||||
IPV4_ADDRESS="${IPV4_ADDRESS%/*}"
|
||||
IPV6_ADDRESS="${IPV6_ADDRESS%/*}"
|
||||
|
||||
# Determine if IPv4/6 addresses exist
|
||||
if [[ -z "${IPV4_ADDRESS}" ]] && [[ -z "${IPV6_ADDRESS}" ]]; then
|
||||
echo -e " ${COL_LIGHT_RED}No IP addresses found! Please run 'pihole -r' to reconfigure${COL_NC}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e " ${COL_LIGHT_RED}Installation Failure: ${setupVars} does not exist! ${COL_NC}
|
||||
Please run 'pihole -r', and choose the 'reconfigure' option to fix."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#Remove the /* from the end of the IPv4addr.
|
||||
IPv4addr=${IPv4_address%/*}
|
||||
|
||||
# Variables for various stages of downloading and formatting the list
|
||||
basename=pihole
|
||||
piholeDir=/etc/${basename}
|
||||
adList=${piholeDir}/gravity.list
|
||||
justDomainsExtension=domains
|
||||
matterAndLight=${basename}.0.matterandlight.txt
|
||||
supernova=${basename}.1.supernova.txt
|
||||
eventHorizon=${basename}.2.eventHorizon.txt
|
||||
accretionDisc=${basename}.3.accretionDisc.txt
|
||||
|
||||
# Warn users still using pihole.conf that it no longer has any effect (I imagine about 2 people use it)
|
||||
if [[ -r ${piholeDir}/pihole.conf ]];then
|
||||
echo "::: pihole.conf file no longer supported. Over-rides in this file are ignored."
|
||||
# Determine if superseded pihole.conf exists
|
||||
if [[ -r "${piholeDir}/pihole.conf" ]]; then
|
||||
echo -e " ${COL_LIGHT_RED}Ignoring overrides specified within pihole.conf! ${COL_NC}"
|
||||
fi
|
||||
|
||||
###########################
|
||||
# collapse - begin formation of pihole
|
||||
gravity_collapse() {
|
||||
echo "::: Neutrino emissions detected..."
|
||||
echo ":::"
|
||||
#Decide if we're using a custom ad block list, or defaults.
|
||||
if [ -f ${adListFile} ]; then
|
||||
#custom file found, use this instead of default
|
||||
echo -n "::: Custom adList file detected. Reading..."
|
||||
sources=()
|
||||
while read -r line; do
|
||||
#Do not read commented out or blank lines
|
||||
if [[ ${line} = \#* ]] || [[ ! ${line} ]]; then
|
||||
echo "" > /dev/null
|
||||
else
|
||||
sources+=(${line})
|
||||
fi
|
||||
done < ${adListFile}
|
||||
echo " done!"
|
||||
else
|
||||
#no custom file found, use defaults!
|
||||
echo -n "::: No custom adlist file detected, reading from default file..."
|
||||
sources=()
|
||||
while read -r line; do
|
||||
#Do not read commented out or blank lines
|
||||
if [[ ${line} = \#* ]] || [[ ! ${line} ]]; then
|
||||
echo "" > /dev/null
|
||||
else
|
||||
sources+=(${line})
|
||||
fi
|
||||
done < ${adListDefault}
|
||||
echo " done!"
|
||||
fi
|
||||
# Determine if DNS resolution is available before proceeding
|
||||
gravity_CheckDNSResolutionAvailable() {
|
||||
local lookupDomain="pi.hole"
|
||||
|
||||
# Create the pihole resource directory if it doesn't exist. Future files will be stored here
|
||||
if [[ -d ${piholeDir} ]];then
|
||||
# Temporary hack to allow non-root access to pihole directory
|
||||
# Will update later, needed for existing installs, new installs should
|
||||
# create this directory as non-root
|
||||
chmod 777 ${piholeDir}
|
||||
echo ":::"
|
||||
echo "::: Existing pihole directory found"
|
||||
else
|
||||
echo "::: Creating pihole directory..."
|
||||
mkdir ${piholeDir}
|
||||
chmod 777 ${piholeDir}
|
||||
fi
|
||||
# Determine if $localList does not exist
|
||||
if [[ ! -e "${localList}" ]]; then
|
||||
lookupDomain="raw.githubusercontent.com"
|
||||
fi
|
||||
|
||||
# Determine if $lookupDomain is resolvable
|
||||
if timeout 1 getent hosts "${lookupDomain}" &> /dev/null; then
|
||||
# Print confirmation of resolvability if it had previously failed
|
||||
if [[ -n "${secs:-}" ]]; then
|
||||
echo -e "${OVER} ${TICK} DNS resolution is now available\\n"
|
||||
fi
|
||||
return 0
|
||||
elif [[ -n "${secs:-}" ]]; then
|
||||
echo -e "${OVER} ${CROSS} DNS resolution is not available"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If the /etc/resolv.conf contains resolvers other than 127.0.0.1 then the local dnsmasq will not be queried and pi.hole is NXDOMAIN.
|
||||
# This means that even though name resolution is working, the getent hosts check fails and the holddown timer keeps ticking and eventualy fails
|
||||
# So we check the output of the last command and if it failed, attempt to use dig +short as a fallback
|
||||
if timeout 1 dig +short "${lookupDomain}" &> /dev/null; then
|
||||
if [[ -n "${secs:-}" ]]; then
|
||||
echo -e "${OVER} ${TICK} DNS resolution is now available\\n"
|
||||
fi
|
||||
return 0
|
||||
elif [[ -n "${secs:-}" ]]; then
|
||||
echo -e "${OVER} ${CROSS} DNS resolution is not available"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine error output message
|
||||
if pidof dnsmasq &> /dev/null; then
|
||||
echo -e " ${CROSS} DNS resolution is currently unavailable"
|
||||
else
|
||||
echo -e " ${CROSS} DNS service is not running"
|
||||
"${PIHOLE_COMMAND}" restartdns
|
||||
fi
|
||||
|
||||
# Ensure DNS server is given time to be resolvable
|
||||
secs="120"
|
||||
echo -ne " ${INFO} Time until retry: ${secs}"
|
||||
until timeout 1 getent hosts "${lookupDomain}" &> /dev/null; do
|
||||
[[ "${secs:-}" -eq 0 ]] && break
|
||||
echo -ne "${OVER} ${INFO} Time until retry: ${secs}"
|
||||
: $((secs--))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Try again
|
||||
gravity_CheckDNSResolutionAvailable
|
||||
}
|
||||
|
||||
# patternCheck - check to see if curl downloaded any new files.
|
||||
gravity_patternCheck() {
|
||||
patternBuffer=$1
|
||||
# check if the patternbuffer is a non-zero length file
|
||||
if [[ -s "$patternBuffer" ]];then
|
||||
# Some of the blocklists are copyright, they need to be downloaded
|
||||
# and stored as is. They can be processed for content after they
|
||||
# have been saved.
|
||||
cp "$patternBuffer" "$saveLocation"
|
||||
echo " List updated, transport successful!"
|
||||
else
|
||||
# curl didn't download any host files, probably because of the date check
|
||||
echo " No changes detected, transport skipped!"
|
||||
fi
|
||||
# Retrieve blocklist URLs and parse domains from adlists.list
|
||||
gravity_GetBlocklistUrls() {
|
||||
echo -e " ${INFO} ${COL_BOLD}Neutrino emissions detected${COL_NC}..."
|
||||
|
||||
# Determine if adlists file needs handling
|
||||
if [[ ! -f "${adListFile}" ]]; then
|
||||
# Create "adlists.list" by copying "adlists.default" from internal core repo
|
||||
cp "${adListRepoDefault}" "${adListFile}" 2> /dev/null || \
|
||||
echo -e " ${CROSS} Unable to copy ${adListFile##*/} from ${piholeRepo}"
|
||||
elif [[ -f "${adListDefault}" ]] && [[ -f "${adListFile}" ]]; then
|
||||
# Remove superceded $adListDefault file
|
||||
rm "${adListDefault}" 2> /dev/null || \
|
||||
echo -e " ${CROSS} Unable to remove ${adListDefault}"
|
||||
fi
|
||||
|
||||
local str="Pulling blocklist source list into range"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
|
||||
# Retrieve source URLs from $adListFile
|
||||
# Logic: Remove comments and empty lines
|
||||
mapfile -t sources <<< "$(grep -v -E "^(#|$)" "${adListFile}" 2> /dev/null)"
|
||||
|
||||
# Parse source domains from $sources
|
||||
mapfile -t sourceDomains <<< "$(
|
||||
# Logic: Split by folder/port
|
||||
awk -F '[/:]' '{
|
||||
# Remove URL protocol & optional username:password@
|
||||
gsub(/(.*:\/\/|.*:.*@)/, "", $0)
|
||||
if(length($1)>0){print $1}
|
||||
else {print "local"}
|
||||
}' <<< "$(printf '%s\n' "${sources[@]}")" 2> /dev/null
|
||||
)"
|
||||
|
||||
if [[ -n "${sources[*]}" ]] && [[ -n "${sourceDomains[*]}" ]]; then
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
else
|
||||
echo -e "${OVER} ${CROSS} ${str}"
|
||||
gravity_Cleanup "error"
|
||||
fi
|
||||
}
|
||||
|
||||
# transport - curl the specified url with any needed command extentions
|
||||
gravity_transport() {
|
||||
url=$1
|
||||
cmd_ext=$2
|
||||
agent=$3
|
||||
# Define options for when retrieving blocklists
|
||||
gravity_SetDownloadOptions() {
|
||||
local url domain agent cmd_ext str
|
||||
|
||||
# tmp file, so we don't have to store the (long!) lists in RAM
|
||||
patternBuffer=$(mktemp)
|
||||
heisenbergCompensator=""
|
||||
if [[ -r ${saveLocation} ]]; then
|
||||
# if domain has been saved, add file for date check to only download newer
|
||||
heisenbergCompensator="-z $saveLocation"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Silently curl url
|
||||
curl -s -L ${cmd_ext} ${heisenbergCompensator} -A "$agent" ${url} > ${patternBuffer}
|
||||
# Check for list updates
|
||||
gravity_patternCheck "$patternBuffer"
|
||||
# Cleanup
|
||||
rm -f "$patternBuffer"
|
||||
# Loop through $sources and download each one
|
||||
for ((i = 0; i < "${#sources[@]}"; i++)); do
|
||||
url="${sources[$i]}"
|
||||
domain="${sourceDomains[$i]}"
|
||||
|
||||
# Save the file as list.#.domain
|
||||
saveLocation="${piholeDir}/list.${i}.${domain}.${domainsExtension}"
|
||||
activeDomains[$i]="${saveLocation}"
|
||||
|
||||
# Default user-agent (for Cloudflare's Browser Integrity Check: https://support.cloudflare.com/hc/en-us/articles/200170086-What-does-the-Browser-Integrity-Check-do-)
|
||||
agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36"
|
||||
|
||||
# Provide special commands for blocklists which may need them
|
||||
case "${domain}" in
|
||||
"pgl.yoyo.org") cmd_ext="-d mimetype=plaintext -d hostformat=hosts";;
|
||||
*) cmd_ext="";;
|
||||
esac
|
||||
|
||||
if [[ "${skipDownload}" == false ]]; then
|
||||
echo -e " ${INFO} Target: ${domain} (${url##*/})"
|
||||
gravity_DownloadBlocklistFromUrl "${url}" "${cmd_ext}" "${agent}"
|
||||
echo ""
|
||||
fi
|
||||
done
|
||||
gravity_Blackbody=true
|
||||
}
|
||||
|
||||
# spinup - main gravity function
|
||||
gravity_spinup() {
|
||||
echo ":::"
|
||||
# Loop through domain list. Download each one and remove commented lines (lines beginning with '# 'or '/') and # blank lines
|
||||
for ((i = 0; i < "${#sources[@]}"; i++))
|
||||
do
|
||||
url=${sources[$i]}
|
||||
# Get just the domain from the URL
|
||||
domain=$(echo "$url" | cut -d'/' -f3)
|
||||
# Download specified URL and perform checks on HTTP status and file content
|
||||
gravity_DownloadBlocklistFromUrl() {
|
||||
local url="${1}" cmd_ext="${2}" agent="${3}" heisenbergCompensator="" patternBuffer str httpCode success=""
|
||||
|
||||
# Save the file as list.#.domain
|
||||
saveLocation=${piholeDir}/list.${i}.${domain}.${justDomainsExtension}
|
||||
activeDomains[$i]=${saveLocation}
|
||||
# Create temp file to store content on disk instead of RAM
|
||||
patternBuffer=$(mktemp -p "/tmp" --suffix=".phgpb")
|
||||
|
||||
agent="Mozilla/10.0"
|
||||
# Determine if $saveLocation has read permission
|
||||
if [[ -r "${saveLocation}" && $url != "file"* ]]; then
|
||||
# Have curl determine if a remote file has been modified since last retrieval
|
||||
# Uses "Last-Modified" header, which certain web servers do not provide (e.g: raw github urls)
|
||||
# Note: Don't do this for local files, always download them
|
||||
heisenbergCompensator="-z ${saveLocation}"
|
||||
fi
|
||||
|
||||
echo -n "::: Getting $domain list..."
|
||||
str="Status:"
|
||||
echo -ne " ${INFO} ${str} Pending..."
|
||||
# shellcheck disable=SC2086
|
||||
httpCode=$(curl -s -L ${cmd_ext} ${heisenbergCompensator} -w "%{http_code}" -A "${agent}" "${url}" -o "${patternBuffer}" 2> /dev/null)
|
||||
|
||||
# Use a case statement to download lists that need special cURL commands
|
||||
# to complete properly and reset the user agent when required
|
||||
case "$domain" in
|
||||
"adblock.mahakala.is")
|
||||
agent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36'
|
||||
cmd_ext="-e http://forum.xda-developers.com/"
|
||||
;;
|
||||
case $url in
|
||||
# Did we "download" a remote file?
|
||||
"http"*)
|
||||
# Determine "Status:" output based on HTTP response
|
||||
case "${httpCode}" in
|
||||
"200") echo -e "${OVER} ${TICK} ${str} Retrieval successful"; success=true;;
|
||||
"304") echo -e "${OVER} ${TICK} ${str} No changes detected"; success=true;;
|
||||
"000") echo -e "${OVER} ${CROSS} ${str} Connection Refused";;
|
||||
"403") echo -e "${OVER} ${CROSS} ${str} Forbidden";;
|
||||
"404") echo -e "${OVER} ${CROSS} ${str} Not found";;
|
||||
"408") echo -e "${OVER} ${CROSS} ${str} Time-out";;
|
||||
"451") echo -e "${OVER} ${CROSS} ${str} Unavailable For Legal Reasons";;
|
||||
"500") echo -e "${OVER} ${CROSS} ${str} Internal Server Error";;
|
||||
"504") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Gateway)";;
|
||||
"521") echo -e "${OVER} ${CROSS} ${str} Web Server Is Down (Cloudflare)";;
|
||||
"522") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Cloudflare)";;
|
||||
* ) echo -e "${OVER} ${CROSS} ${str} ${httpCode}";;
|
||||
esac;;
|
||||
# Did we "download" a local file?
|
||||
"file"*)
|
||||
if [[ -s "${patternBuffer}" ]]; then
|
||||
echo -e "${OVER} ${TICK} ${str} Retrieval successful"; success=true
|
||||
else
|
||||
echo -e "${OVER} ${CROSS} ${str} Not found / empty list"
|
||||
fi;;
|
||||
*) echo -e "${OVER} ${CROSS} ${str} ${url} ${httpCode}";;
|
||||
esac
|
||||
|
||||
"pgl.yoyo.org")
|
||||
cmd_ext="-d mimetype=plaintext -d hostformat=hosts"
|
||||
;;
|
||||
|
||||
# Default is a simple request
|
||||
*) cmd_ext=""
|
||||
esac
|
||||
gravity_transport "$url" "$cmd_ext" "$agent"
|
||||
done
|
||||
# Determine if the blocklist was downloaded and saved correctly
|
||||
if [[ "${success}" == true ]]; then
|
||||
if [[ "${httpCode}" == "304" ]]; then
|
||||
: # Do not attempt to re-parse file
|
||||
# Check if $patternbuffer is a non-zero length file
|
||||
elif [[ -s "${patternBuffer}" ]]; then
|
||||
# Determine if blocklist is non-standard and parse as appropriate
|
||||
gravity_ParseFileIntoDomains "${patternBuffer}" "${saveLocation}"
|
||||
else
|
||||
# Fall back to previously cached list if $patternBuffer is empty
|
||||
echo -e " ${INFO} Received empty file: ${COL_LIGHT_GREEN}using previously cached list${COL_NC}"
|
||||
fi
|
||||
else
|
||||
# Determine if cached list has read permission
|
||||
if [[ -r "${saveLocation}" ]]; then
|
||||
echo -e " ${CROSS} List download failed: ${COL_LIGHT_GREEN}using previously cached list${COL_NC}"
|
||||
else
|
||||
echo -e " ${CROSS} List download failed: ${COL_LIGHT_RED}no cached list available${COL_NC}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Schwarzchild - aggregate domains to one list and add blacklisted domains
|
||||
gravity_Schwarzchild() {
|
||||
echo "::: "
|
||||
# Find all active domains and compile them into one file and remove CRs
|
||||
echo -n "::: Aggregating list of domains..."
|
||||
truncate -s 0 ${piholeDir}/${matterAndLight}
|
||||
for i in "${activeDomains[@]}"
|
||||
do
|
||||
cat "$i" | tr -d '\r' >> ${piholeDir}/${matterAndLight}
|
||||
done
|
||||
echo " done!"
|
||||
# Parse source files into domains format
|
||||
gravity_ParseFileIntoDomains() {
|
||||
local source="${1}" destination="${2}" firstLine abpFilter
|
||||
|
||||
# Determine if we are parsing a consolidated list
|
||||
if [[ "${source}" == "${piholeDir}/${matterAndLight}" ]]; then
|
||||
# Remove comments and print only the domain name
|
||||
# Most of the lists downloaded are already in hosts file format but the spacing/formating is not contigious
|
||||
# This helps with that and makes it easier to read
|
||||
# It also helps with debugging so each stage of the script can be researched more in depth
|
||||
#Awk -F splits on given IFS, we grab the right hand side (chops trailing #coments and /'s to grab the domain only.
|
||||
#Last awk command takes non-commented lines and if they have 2 fields, take the left field (the domain) and leave
|
||||
#+ the right (IP address), otherwise grab the single field.
|
||||
|
||||
< ${source} awk -F '#' '{print $1}' | \
|
||||
awk -F '/' '{print $1}' | \
|
||||
awk '($1 !~ /^#/) { if (NF>1) {print $2} else {print $1}}' | \
|
||||
sed -nr -e 's/\.{2,}/./g' -e '/\./p' > ${destination}
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Individual file parsing: Keep comments, while parsing domains from each line
|
||||
# We keep comments to respect the list maintainer's licensing
|
||||
read -r firstLine < "${source}"
|
||||
|
||||
# Determine how to parse individual source file formats
|
||||
if [[ "${firstLine,,}" =~ (adblock|ublock|^!) ]]; then
|
||||
# Compare $firstLine against lower case words found in Adblock lists
|
||||
echo -ne " ${INFO} Format: Adblock"
|
||||
|
||||
# Define symbols used as comments: [!
|
||||
# "||.*^" includes the "Example 2" domains we can extract
|
||||
# https://adblockplus.org/filter-cheatsheet
|
||||
abpFilter="/^(\\[|!)|^(\\|\\|.*\\^)/"
|
||||
|
||||
# Parse Adblock lists by extracting "Example 2" domains
|
||||
# Logic: Ignore lines which do not include comments or domain name anchor
|
||||
awk ''"${abpFilter}"' {
|
||||
# Remove valid adblock type options
|
||||
gsub(/\$?~?(important|third-party|popup|subdocument|websocket),?/, "", $0)
|
||||
# Remove starting domain name anchor "||" and ending seperator "^"
|
||||
gsub(/^(\|\|)|(\^)/, "", $0)
|
||||
# Remove invalid characters (*/,=$)
|
||||
if($0 ~ /[*\/,=\$]/) { $0="" }
|
||||
# Remove lines which are only IPv4 addresses
|
||||
if($0 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) { $0="" }
|
||||
if($0) { print $0 }
|
||||
}' "${source}" > "${destination}"
|
||||
|
||||
# Determine if there are Adblock exception rules
|
||||
# https://adblockplus.org/filters
|
||||
if grep -q "^@@||" "${source}" &> /dev/null; then
|
||||
# Parse Adblock lists by extracting exception rules
|
||||
# Logic: Ignore lines which do not include exception format "@@||example.com^"
|
||||
awk -F "[|^]" '/^@@\|\|.*\^/ {
|
||||
# Remove valid adblock type options
|
||||
gsub(/\$?~?(third-party)/, "", $0)
|
||||
# Remove invalid characters (*/,=$)
|
||||
if($0 ~ /[*\/,=\$]/) { $0="" }
|
||||
if($3) { print $3 }
|
||||
}' "${source}" > "${destination}.exceptionsFile.tmp"
|
||||
|
||||
# Remove exceptions
|
||||
comm -23 "${destination}" <(sort "${destination}.exceptionsFile.tmp") > "${source}"
|
||||
mv "${source}" "${destination}"
|
||||
fi
|
||||
|
||||
echo -e "${OVER} ${TICK} Format: Adblock"
|
||||
elif grep -q "^address=/" "${source}" &> /dev/null; then
|
||||
# Parse Dnsmasq format lists
|
||||
echo -e " ${CROSS} Format: Dnsmasq (list type not supported)"
|
||||
elif grep -q -E "^https?://" "${source}" &> /dev/null; then
|
||||
# Parse URL list if source file contains "http://" or "https://"
|
||||
# Scanning for "^IPv4$" is too slow with large (1M) lists on low-end hardware
|
||||
echo -ne " ${INFO} Format: URL"
|
||||
|
||||
awk '{
|
||||
# Remove URL protocol, optional "username:password@", and ":?/;"
|
||||
if ($0 ~ /[:?\/;]/) { gsub(/(^.*:\/\/(.*:.*@)?|[:?\/;].*)/, "", $0) }
|
||||
# Remove lines which are only IPv4 addresses
|
||||
if ($0 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) { $0="" }
|
||||
if ($0) { print $0 }
|
||||
}' "${source}" 2> /dev/null > "${destination}"
|
||||
|
||||
echo -e "${OVER} ${TICK} Format: URL"
|
||||
else
|
||||
# Default: Keep hosts/domains file in same format as it was downloaded
|
||||
output=$( { mv "${source}" "${destination}"; } 2>&1 )
|
||||
|
||||
if [[ ! -e "${destination}" ]]; then
|
||||
echo -e "\\n ${CROSS} Unable to move tmp file to ${piholeDir}
|
||||
${output}"
|
||||
gravity_Cleanup "error"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
gravity_Blacklist(){
|
||||
# Append blacklist entries if they exist
|
||||
echo -n "::: Running blacklist script to update HOSTS file...."
|
||||
${blacklistScript} -f -nr -q > /dev/null
|
||||
# Create (unfiltered) "Matter and Light" consolidated list
|
||||
gravity_ConsolidateDownloadedBlocklists() {
|
||||
local str lastLine
|
||||
|
||||
numBlacklisted=$(wc -l < "/etc/pihole/blacklist.txt")
|
||||
plural=; [[ "$numBlacklisted" != "1" ]] && plural=s
|
||||
echo " $numBlacklisted domain${plural} blacklisted!"
|
||||
str="Consolidating blocklists"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
|
||||
# Empty $matterAndLight if it already exists, otherwise, create it
|
||||
: > "${piholeDir}/${matterAndLight}"
|
||||
|
||||
# Loop through each *.domains file
|
||||
for i in "${activeDomains[@]}"; do
|
||||
# Determine if file has read permissions, as download might have failed
|
||||
if [[ -r "${i}" ]]; then
|
||||
# Remove windows CRs from file, convert list to lower case, and append into $matterAndLight
|
||||
tr -d '\r' < "${i}" | tr '[:upper:]' '[:lower:]' >> "${piholeDir}/${matterAndLight}"
|
||||
|
||||
# Ensure that the first line of a new list is on a new line
|
||||
lastLine=$(tail -1 "${piholeDir}/${matterAndLight}")
|
||||
if [[ "${#lastLine}" -gt 0 ]]; then
|
||||
echo "" >> "${piholeDir}/${matterAndLight}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
}
|
||||
|
||||
# Parse consolidated list into (filtered, unique) domains-only format
|
||||
gravity_SortAndFilterConsolidatedList() {
|
||||
local str num
|
||||
|
||||
str="Extracting domains from blocklists"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
|
||||
# Parse into hosts file
|
||||
gravity_ParseFileIntoDomains "${piholeDir}/${matterAndLight}" "${piholeDir}/${parsedMatter}"
|
||||
|
||||
# Format $parsedMatter line total as currency
|
||||
num=$(printf "%'.0f" "$(wc -l < "${piholeDir}/${parsedMatter}")")
|
||||
echo -e "${OVER} ${TICK} ${str}
|
||||
${INFO} Number of domains being pulled in by gravity: ${COL_BLUE}${num}${COL_NC}"
|
||||
|
||||
str="Removing duplicate domains"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
sort -u "${piholeDir}/${parsedMatter}" > "${piholeDir}/${preEventHorizon}"
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
|
||||
# Format $preEventHorizon line total as currency
|
||||
num=$(printf "%'.0f" "$(wc -l < "${piholeDir}/${preEventHorizon}")")
|
||||
echo -e " ${INFO} Number of unique domains trapped in the Event Horizon: ${COL_BLUE}${num}${COL_NC}"
|
||||
}
|
||||
|
||||
# Whitelist unique blocklist domain sources
|
||||
gravity_WhitelistBlocklistSourceUrls() {
|
||||
local uniqDomains str
|
||||
|
||||
echo ""
|
||||
|
||||
# Create array of unique $sourceDomains
|
||||
mapfile -t uniqDomains <<< "$(awk '{ if(!a[$1]++) { print $1 } }' <<< "$(printf '%s\n' "${sourceDomains[@]}")")"
|
||||
|
||||
str="Number of blocklist source domains being added to the whitelist: ${#uniqDomains[@]}"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
|
||||
# Whitelist $uniqDomains
|
||||
"${PIHOLE_COMMAND}" -w -nr -q ${uniqDomains[*]} &> /dev/null
|
||||
|
||||
echo -e "${OVER} ${INFO} ${str}"
|
||||
}
|
||||
|
||||
# Whitelist user-defined domains
|
||||
gravity_Whitelist() {
|
||||
echo ":::"
|
||||
# Prevent our sources from being pulled into the hole
|
||||
plural=; [[ "${sources[@]}" != "1" ]] && plural=s
|
||||
echo -n "::: Adding ${#sources[@]} adlist source${plural} to the whitelist..."
|
||||
local num str
|
||||
|
||||
urls=()
|
||||
for url in "${sources[@]}"
|
||||
do
|
||||
tmp=$(echo "$url" | awk -F '/' '{print $3}')
|
||||
urls=("${urls[@]}" ${tmp})
|
||||
done
|
||||
echo " done!"
|
||||
if [[ ! -f "${whitelistFile}" ]]; then
|
||||
echo -e " ${INFO} Nothing to whitelist!"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo -n "::: Running whitelist script to update HOSTS file...."
|
||||
${whitelistScript} -f -nr -q "${urls[@]}" > /dev/null
|
||||
numWhitelisted=$(wc -l < "/etc/pihole/whitelist.txt")
|
||||
plural=; [[ "$numWhitelisted" != "1" ]] && plural=s
|
||||
echo " $numWhitelisted domain${plural} whitelisted!"
|
||||
num=$(wc -l < "${whitelistFile}")
|
||||
str="Number of whitelisted domains: ${num}"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
|
||||
# Print everything from preEventHorizon into whitelistMatter EXCEPT domains in $whitelistFile
|
||||
comm -23 "${piholeDir}/${preEventHorizon}" <(sort "${whitelistFile}") > "${piholeDir}/${whitelistMatter}"
|
||||
|
||||
echo -e "${OVER} ${INFO} ${str}"
|
||||
}
|
||||
|
||||
gravity_unique() {
|
||||
# Sort and remove duplicates
|
||||
echo -n "::: Removing duplicate domains...."
|
||||
sort -u ${piholeDir}/${supernova} > ${piholeDir}/${eventHorizon}
|
||||
echo " done!"
|
||||
numberOf=$(wc -l < ${piholeDir}/${eventHorizon})
|
||||
echo "::: $numberOf unique domains trapped in the event horizon."
|
||||
# Output count of blacklisted domains and wildcards
|
||||
gravity_ShowBlockCount() {
|
||||
local num
|
||||
|
||||
if [[ -f "${blacklistFile}" ]]; then
|
||||
num=$(printf "%'.0f" "$(wc -l < "${blacklistFile}")")
|
||||
echo -e " ${INFO} Number of blacklisted domains: ${num}"
|
||||
fi
|
||||
|
||||
if [[ -f "${wildcardFile}" ]]; then
|
||||
num=$(grep -c "^" "${wildcardFile}")
|
||||
# If IPv4 and IPv6 is used, divide total wildcard count by 2
|
||||
if [[ -n "${IPV4_ADDRESS}" ]] && [[ -n "${IPV6_ADDRESS}" ]];then
|
||||
num=$(( num/2 ))
|
||||
fi
|
||||
echo -e " ${INFO} Number of wildcard blocked domains: ${num}"
|
||||
fi
|
||||
}
|
||||
|
||||
gravity_hostFormat() {
|
||||
# Format domain list as "192.168.x.x domain.com"
|
||||
echo "::: Formatting domains into a HOSTS file..."
|
||||
if [[ -f /etc/hostname ]]; then
|
||||
hostname=$(</etc/hostname)
|
||||
elif [ -x "$(command -v hostname)" ]; then
|
||||
hostname=$(hostname -f)
|
||||
else
|
||||
echo "::: Error: Unable to determine fully qualified domain name of host"
|
||||
fi
|
||||
# If there is a value in the $piholeIPv6, then IPv6 will be used, so the awk command modified to create a line for both protocols
|
||||
if [[ -n "${IPv6_address}" ]];then
|
||||
# Add hostname and dummy domain to the top of gravity.list to make ping result return a friendlier looking domain! Also allows for an easy way to access the Pi-hole admin console (pi.hole/admin)
|
||||
echo -e "$IPv4addr $hostname\n$IPv6_address $hostname\n$IPv4addr pi.hole\n$IPv6_address pi.hole" > ${piholeDir}/${accretionDisc}
|
||||
cat ${piholeDir}/${eventHorizon} | awk -v ipv4addr="$IPv4addr" -v ipv6addr="$IPv6_address" '{sub(/\r$/,""); print ipv4addr" "$0"\n"ipv6addr" "$0}' >> ${piholeDir}/${accretionDisc}
|
||||
else
|
||||
# Otherwise, just create gravity.list as normal using IPv4
|
||||
# Add hostname and dummy domain to the top of gravity.list to make ping result return a friendlier looking domain! Also allows for an easy way to access the Pi-hole admin console (pi.hole/admin)
|
||||
echo -e "$IPv4addr $hostname\n$IPv4addr pi.hole" > ${piholeDir}/${accretionDisc}
|
||||
cat ${piholeDir}/${eventHorizon} | awk -v ipv4addr="$IPv4addr" '{sub(/\r$/,""); print ipv4addr" "$0}' >> ${piholeDir}/${accretionDisc}
|
||||
fi
|
||||
|
||||
# Copy the file over as /etc/pihole/gravity.list so dnsmasq can use it
|
||||
cp ${piholeDir}/${accretionDisc} ${adList}
|
||||
# Parse list of domains into hosts format
|
||||
gravity_ParseDomainsIntoHosts() {
|
||||
awk -v ipv4="$IPV4_ADDRESS" -v ipv6="$IPV6_ADDRESS" '{
|
||||
# Remove windows CR line endings
|
||||
sub(/\r$/, "")
|
||||
# Parse each line as "ipaddr domain"
|
||||
if(ipv6 && ipv4) {
|
||||
print ipv4" "$0"\n"ipv6" "$0
|
||||
} else if(!ipv6) {
|
||||
print ipv4" "$0
|
||||
} else {
|
||||
print ipv6" "$0
|
||||
}
|
||||
}' >> "${2}" < "${1}"
|
||||
}
|
||||
|
||||
# blackbody - remove any remnant files from script processes
|
||||
gravity_blackbody() {
|
||||
# Loop through list files
|
||||
for file in ${piholeDir}/*.${justDomainsExtension}
|
||||
do
|
||||
# If list is in active array then leave it (noop) else rm the list
|
||||
if [[ " ${activeDomains[@]} " =~ ${file} ]]; then
|
||||
:
|
||||
else
|
||||
rm -f "$file"
|
||||
fi
|
||||
done
|
||||
# Create "localhost" entries into hosts format
|
||||
gravity_ParseLocalDomains() {
|
||||
local hostname
|
||||
|
||||
if [[ -s "/etc/hostname" ]]; then
|
||||
hostname=$(< "/etc/hostname")
|
||||
elif command -v hostname &> /dev/null; then
|
||||
hostname=$(hostname -f)
|
||||
else
|
||||
echo -e " ${CROSS} Unable to determine fully qualified domain name of host"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo -e "${hostname}\\npi.hole" > "${localList}.tmp"
|
||||
|
||||
# Empty $localList if it already exists, otherwise, create it
|
||||
: > "${localList}"
|
||||
|
||||
gravity_ParseDomainsIntoHosts "${localList}.tmp" "${localList}"
|
||||
|
||||
# Add additional LAN hosts provided by OpenVPN (if available)
|
||||
if [[ -f "${VPNList}" ]]; then
|
||||
awk -F, '{printf $2"\t"$1".vpn\n"}' "${VPNList}" >> "${localList}"
|
||||
fi
|
||||
}
|
||||
|
||||
gravity_advanced() {
|
||||
# Remove comments and print only the domain name
|
||||
# Most of the lists downloaded are already in hosts file format but the spacing/formating is not contigious
|
||||
# This helps with that and makes it easier to read
|
||||
# It also helps with debugging so each stage of the script can be researched more in depth
|
||||
echo -n "::: Formatting list of domains to remove comments...."
|
||||
#awk '($1 !~ /^#/) { if (NF>1) {print $2} else {print $1}}' ${piholeDir}/${matterAndLight} | sed -nr -e 's/\.{2,}/./g' -e '/\./p' > ${piholeDir}/${supernova}
|
||||
#Above line does not correctly grab domains where comment is on the same line (e.g 'addomain.com #comment')
|
||||
#Add additional awk command to read all lines up to a '#', and then continue as we were
|
||||
cat ${piholeDir}/${matterAndLight} | awk -F'#' '{print $1}' | awk '($1 !~ /^#/) { if (NF>1) {print $2} else {print $1}}' | sed -nr -e 's/\.{2,}/./g' -e '/\./p' > ${piholeDir}/${supernova}
|
||||
echo " done!"
|
||||
# Create primary blacklist entries
|
||||
gravity_ParseBlacklistDomains() {
|
||||
local output status
|
||||
|
||||
numberOf=$(wc -l < ${piholeDir}/${supernova})
|
||||
echo "::: $numberOf domains being pulled in by gravity..."
|
||||
# Empty $accretionDisc if it already exists, otherwise, create it
|
||||
: > "${piholeDir}/${accretionDisc}"
|
||||
|
||||
gravity_unique
|
||||
gravity_ParseDomainsIntoHosts "${piholeDir}/${whitelistMatter}" "${piholeDir}/${accretionDisc}"
|
||||
|
||||
# Move the file over as /etc/pihole/gravity.list so dnsmasq can use it
|
||||
output=$( { mv "${piholeDir}/${accretionDisc}" "${adList}"; } 2>&1 )
|
||||
status="$?"
|
||||
|
||||
if [[ "${status}" -ne 0 ]]; then
|
||||
echo -e "\\n ${CROSS} Unable to move ${accretionDisc} from ${piholeDir}\\n ${output}"
|
||||
gravity_Cleanup "error"
|
||||
fi
|
||||
}
|
||||
|
||||
gravity_reload() {
|
||||
#Clear no longer needed files...
|
||||
echo ":::"
|
||||
echo -n "::: Cleaning up un-needed files..."
|
||||
rm ${piholeDir}/pihole.*.txt
|
||||
echo " done!"
|
||||
# Create user-added blacklist entries
|
||||
gravity_ParseUserDomains() {
|
||||
if [[ ! -f "${blacklistFile}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Reload hosts file
|
||||
echo ":::"
|
||||
echo -n "::: Refresh lists in dnsmasq..."
|
||||
|
||||
#ensure /etc/dnsmasq.d/01-pihole.conf is pointing at the correct list!
|
||||
#First escape forward slashes in the path:
|
||||
adList=${adList//\//\\\/}
|
||||
#Now replace the line in dnsmasq file
|
||||
sed -i "s/^addn-hosts.*/addn-hosts=$adList/" /etc/dnsmasq.d/01-pihole.conf
|
||||
find "$piholeDir" -type f -exec chmod 666 {} \;
|
||||
|
||||
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
|
||||
else
|
||||
# service not running, start it up
|
||||
if [ -x "$(command -v systemctl)" ]; then
|
||||
systemctl start dnsmasq
|
||||
else
|
||||
service dnsmasq start
|
||||
fi
|
||||
fi
|
||||
gravity_ParseDomainsIntoHosts "${blacklistFile}" "${blackList}.tmp"
|
||||
# Copy the file over as /etc/pihole/black.list so dnsmasq can use it
|
||||
mv "${blackList}.tmp" "${blackList}" 2> /dev/null || \
|
||||
echo -e "\\n ${CROSS} Unable to move ${blackList##*/}.tmp to ${piholeDir}"
|
||||
}
|
||||
|
||||
# Trap Ctrl-C
|
||||
gravity_Trap() {
|
||||
trap '{ echo -e "\\n\\n ${INFO} ${COL_LIGHT_RED}User-abort detected${COL_NC}"; gravity_Cleanup "error"; }' INT
|
||||
}
|
||||
|
||||
for var in "$@"
|
||||
do
|
||||
case "$var" in
|
||||
"-f" | "--force" ) forceGrav=true;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
# Clean up after Gravity upon exit or cancellation
|
||||
gravity_Cleanup() {
|
||||
local error="${1:-}"
|
||||
|
||||
str="Cleaning up stray matter"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
|
||||
# Delete tmp content generated by Gravity
|
||||
rm ${piholeDir}/pihole.*.txt 2> /dev/null
|
||||
rm ${piholeDir}/*.tmp 2> /dev/null
|
||||
rm /tmp/*.phgpb 2> /dev/null
|
||||
|
||||
# Ensure this function only runs when gravity_SetDownloadOptions() has completed
|
||||
if [[ "${gravity_Blackbody:-}" == true ]]; then
|
||||
# Remove any unused .domains files
|
||||
for file in ${piholeDir}/*.${domainsExtension}; do
|
||||
# If list is not in active array, then remove it
|
||||
if [[ ! "${activeDomains[*]}" == *"${file}"* ]]; then
|
||||
rm -f "${file}" 2> /dev/null || \
|
||||
echo -e " ${CROSS} Failed to remove ${file##*/}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
|
||||
# Only restart DNS service if offline
|
||||
if ! pidof dnsmasq &> /dev/null; then
|
||||
"${PIHOLE_COMMAND}" restartdns
|
||||
dnsWasOffline=true
|
||||
fi
|
||||
|
||||
# Print Pi-hole status if an error occured
|
||||
if [[ -n "${error}" ]]; then
|
||||
"${PIHOLE_COMMAND}" status
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
helpFunc() {
|
||||
echo "Usage: pihole -g
|
||||
Update domains from blocklists specified in adlists.list
|
||||
|
||||
Options:
|
||||
-f, --force Force the download of all specified blocklists
|
||||
-h, --help Show this help dialog"
|
||||
exit 0
|
||||
}
|
||||
|
||||
for var in "$@"; do
|
||||
case "${var}" in
|
||||
"-f" | "--force" ) forceDelete=true;;
|
||||
"-h" | "--help" ) helpFunc;;
|
||||
"-sd" | "--skip-download" ) skipDownload=true;;
|
||||
"-b" | "--blacklist-only" ) listType="blacklist";;
|
||||
"-w" | "--whitelist-only" ) listType="whitelist";;
|
||||
"-wild" | "--wildcard-only" ) listType="wildcard"; dnsRestartType="restart";;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ${forceGrav} == true ]]; then
|
||||
echo -n "::: Deleting exising list cache..."
|
||||
rm /etc/pihole/list.*
|
||||
echo " done!"
|
||||
# Trap Ctrl-C
|
||||
gravity_Trap
|
||||
|
||||
if [[ "${forceDelete:-}" == true ]]; then
|
||||
str="Deleting existing list cache"
|
||||
echo -ne "${INFO} ${str}..."
|
||||
|
||||
rm /etc/pihole/list.* 2> /dev/null || true
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
fi
|
||||
|
||||
#Overwrite adlists.default from /etc/.pihole in case any changes have been made. Changes should be saved in /etc/adlists.list
|
||||
cp /etc/.pihole/adlists.default /etc/pihole/adlists.default
|
||||
gravity_collapse
|
||||
gravity_spinup
|
||||
gravity_Schwarzchild
|
||||
gravity_advanced
|
||||
gravity_hostFormat
|
||||
gravity_blackbody
|
||||
gravity_Whitelist
|
||||
gravity_Blacklist
|
||||
gravity_reload
|
||||
# Determine which functions to run
|
||||
if [[ "${skipDownload}" == false ]]; then
|
||||
# Gravity needs to download blocklists
|
||||
gravity_CheckDNSResolutionAvailable
|
||||
gravity_GetBlocklistUrls
|
||||
gravity_SetDownloadOptions
|
||||
gravity_ConsolidateDownloadedBlocklists
|
||||
gravity_SortAndFilterConsolidatedList
|
||||
gravity_WhitelistBlocklistSourceUrls
|
||||
else
|
||||
# Gravity needs to modify Blacklist/Whitelist/Wildcards
|
||||
echo -e " ${INFO} Using cached Event Horizon list..."
|
||||
numberOf=$(printf "%'.0f" "$(wc -l < "${piholeDir}/${preEventHorizon}")")
|
||||
echo -e " ${INFO} ${COL_BLUE}${numberOf}${COL_NC} unique domains trapped in the Event Horizon"
|
||||
fi
|
||||
|
||||
# Perform when downloading blocklists, or modifying the whitelist
|
||||
if [[ "${skipDownload}" == false ]] || [[ "${listType}" == "whitelist" ]]; then
|
||||
gravity_Whitelist
|
||||
fi
|
||||
|
||||
gravity_ShowBlockCount
|
||||
|
||||
# Perform when downloading blocklists, or modifying the white/blacklist (not wildcards)
|
||||
if [[ "${skipDownload}" == false ]] || [[ "${listType}" == *"list" ]]; then
|
||||
str="Parsing domains into hosts format"
|
||||
echo -ne " ${INFO} ${str}..."
|
||||
|
||||
gravity_ParseUserDomains
|
||||
|
||||
# Perform when downloading blocklists
|
||||
if [[ ! "${listType:-}" == "blacklist" ]]; then
|
||||
gravity_ParseLocalDomains
|
||||
gravity_ParseBlacklistDomains
|
||||
fi
|
||||
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
|
||||
gravity_Cleanup
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Determine if DNS has been restarted by this instance of gravity
|
||||
if [[ -z "${dnsWasOffline:-}" ]]; then
|
||||
# Use "force-reload" when restarting dnsmasq for everything but Wildcards
|
||||
"${PIHOLE_COMMAND}" restartdns "${dnsRestartType:-force-reload}"
|
||||
fi
|
||||
"${PIHOLE_COMMAND}" status
|
||||
|
761
pihole
761
pihole
@@ -1,204 +1,667 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Controller for all pihole scripts and functions.
|
||||
#
|
||||
# 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.
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# 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 colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE"
|
||||
source "${colfile}"
|
||||
|
||||
# Must be root to use this tool
|
||||
if [[ ! $EUID -eq 0 ]];then
|
||||
if [ -x "$(command -v sudo)" ];then
|
||||
echo "::: Elevating to root with sudo"
|
||||
exec sudo bash "$0" "$@"
|
||||
exit $?
|
||||
else
|
||||
echo "::: sudo is needed to run pihole commands. Please run this script as root or install sudo."
|
||||
exit 1
|
||||
fi
|
||||
if [[ -x "$(command -v sudo)" ]]; then
|
||||
exec sudo bash "$0" "$@"
|
||||
exit $?
|
||||
else
|
||||
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 "${PI_HOLE_SCRIPT_DIR}/webpage.sh"
|
||||
main "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
whitelistFunc() {
|
||||
shift
|
||||
/opt/pihole/whitelist.sh "$@"
|
||||
exit 0
|
||||
"${PI_HOLE_SCRIPT_DIR}"/list.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
blacklistFunc() {
|
||||
shift
|
||||
/opt/pihole/blacklist.sh "$@"
|
||||
exit 0
|
||||
"${PI_HOLE_SCRIPT_DIR}"/list.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
wildcardFunc() {
|
||||
"${PI_HOLE_SCRIPT_DIR}"/list.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
debugFunc() {
|
||||
/opt/pihole/piholeDebug.sh
|
||||
exit 0
|
||||
local automated
|
||||
local web
|
||||
|
||||
# Pull off the `debug` leaving passed call augmentation flags in $1
|
||||
shift
|
||||
if [[ "$@" == *"-a"* ]]; then
|
||||
automated="true"
|
||||
fi
|
||||
if [[ "$@" == *"-w"* ]]; then
|
||||
web="true"
|
||||
fi
|
||||
|
||||
AUTOMATED=${automated:-} WEBCALL=${web:-} "${PI_HOLE_SCRIPT_DIR}"/piholeDebug.sh
|
||||
exit 0
|
||||
}
|
||||
|
||||
flushFunc() {
|
||||
/opt/pihole/piholeLogFlush.sh
|
||||
exit 0
|
||||
"${PI_HOLE_SCRIPT_DIR}"/piholeLogFlush.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
updatePiholeFunc() {
|
||||
|
||||
if [ ! -d "/etc/.pihole" ]; then #This is unlikely
|
||||
echo "::: Critical Error: Pi-Hole repo missing from system!"
|
||||
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole"
|
||||
exit 1;
|
||||
fi
|
||||
if [ ! -d "/var/www/html/admin" ]; then #This is unlikely
|
||||
echo "::: Critical Error: Pi-Hole repo missing from system!"
|
||||
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
echo "::: Checking for updates..."
|
||||
piholeVersion=$(cd /etc/.pihole/ && git describe --tags --abbrev=0)
|
||||
piholeVersionLatest=$(curl -s https://api.github.com/repos/pi-hole/pi-hole/releases/latest | grep -Po '"tag_name":.*?[^\\]",' | perl -pe 's/"tag_name": "//; s/^"//; s/",$//')
|
||||
|
||||
webVersion=$(cd /var/www/html/admin/ && git describe --tags --abbrev=0)
|
||||
webVersionLatest=$(curl -s https://api.github.com/repos/pi-hole/AdminLTE/releases/latest | grep -Po '"tag_name":.*?[^\\]",' | perl -pe 's/"tag_name": "//; s/^"//; s/",$//')
|
||||
|
||||
echo "::: Pi-hole version is $piholeVersion (Latest version is $piholeVersionLatest)"
|
||||
echo "::: Web Admin version is $webVersion (Latest version is $webVersionLatest)"
|
||||
echo ":::"
|
||||
|
||||
if [[ ${piholeVersion} == ${piholeVersionLatest} ]] ; then
|
||||
echo "::: Pi-hole Base files are already up to date! Version: ${piholeVersionLatest}"
|
||||
echo "::: No need to update!"
|
||||
echo ":::"
|
||||
|
||||
if [[ ${webVersion} == ${webVersionLatest} ]] ; then
|
||||
echo "::: Web Admin files are already up to date!"
|
||||
echo "::: No need to update!"
|
||||
echo ":::"
|
||||
else
|
||||
echo "::: An Update is available for the Web Admin!"
|
||||
echo ":::"
|
||||
echo "::: Fetching latest changes from GitHub..."
|
||||
cd /var/www/html/admin
|
||||
git pull origin master
|
||||
echo ":::"
|
||||
echo "::: Pi-hole Web Admin has been updated to ${webVersion}"
|
||||
echo "::: See https://changes.pi-hole.net for details"
|
||||
fi
|
||||
else
|
||||
echo -n "::: An update is available for "
|
||||
if [[ ${webVersion} == ${webVersionLatest} ]] ; then
|
||||
echo " Pi-Hole!"
|
||||
else
|
||||
echo " Pi-Hole base files and the Web Admin. Both will be updated!"
|
||||
fi
|
||||
|
||||
echo "::: Fetching latest changes from GitHub..."
|
||||
cd /etc/.pihole
|
||||
git pull origin master
|
||||
/etc/.pihole/automated\ install/basic-install.sh --unattended
|
||||
|
||||
echo ":::"
|
||||
echo "::: Pi-hole has been updated to version ${piholeVersionLatest}"
|
||||
if [[ ${webVersion} != ${webVersionLatest} ]] ; then
|
||||
echo "::: Web Admin has been updated to version ${webVersionLatest}"
|
||||
fi
|
||||
echo ":::"
|
||||
echo "::: See https://changes.pi-hole.net for details"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
"${PI_HOLE_SCRIPT_DIR}"/update.sh
|
||||
exit 0
|
||||
}
|
||||
|
||||
reconfigurePiholeFunc() {
|
||||
/etc/.pihole/automated\ install/basic-install.sh --reconfigure
|
||||
exit 0;
|
||||
/etc/.pihole/automated\ install/basic-install.sh --reconfigure
|
||||
exit 0;
|
||||
}
|
||||
|
||||
updateGravityFunc() {
|
||||
/opt/pihole/gravity.sh "$@"
|
||||
exit 0
|
||||
"${PI_HOLE_SCRIPT_DIR}"/gravity.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
setupLCDFunction() {
|
||||
/opt/pihole/setupLCD.sh
|
||||
exit 0
|
||||
# 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 "/etc/pihole" || 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;;
|
||||
"wc" ) grep -i -o -m 1 "/${domain}/" ${lists};;
|
||||
* ) grep -i "${domain}" ${lists} /dev/null;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 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
|
||||
}
|
||||
|
||||
queryFunc() {
|
||||
domain=$2
|
||||
for list in /etc/pihole/list.*
|
||||
do
|
||||
count=$(grep ${domain} $list | wc -l)
|
||||
echo "::: ${list} (${count} results)"
|
||||
if [[ ${count} > 0 ]]; then
|
||||
grep ${domain} ${list}
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
shift
|
||||
local options="$*" adlist="" all="" exact="" blockpage="" matchType="match"
|
||||
|
||||
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 "/etc/pihole/adlists.list" ]]; then
|
||||
echo -e "${COL_LIGHT_RED}The file '/etc/pihole/adlists.list' 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 "/etc/pihole" || 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} found within 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 $(< "/etc/pihole/adlists.list"); 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
|
||||
}
|
||||
|
||||
chronometerFunc() {
|
||||
shift
|
||||
/opt/pihole/chronometer.sh "$@"
|
||||
exit 0
|
||||
shift
|
||||
"${PI_HOLE_SCRIPT_DIR}"/chronometer.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
uninstallFunc() {
|
||||
/opt/pihole/uninstall.sh
|
||||
exit 0
|
||||
"${PI_HOLE_SCRIPT_DIR}"/uninstall.sh
|
||||
exit 0
|
||||
}
|
||||
|
||||
versionFunc() {
|
||||
/opt/pihole/version.sh
|
||||
shift
|
||||
"${PI_HOLE_SCRIPT_DIR}"/version.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
restartDNS() {
|
||||
local svcOption svc str output status
|
||||
svcOption="${1:-}"
|
||||
|
||||
# Determine if we should reload or restart dnsmasq
|
||||
if [[ "${svcOption}" =~ "reload" ]]; then
|
||||
# Using SIGHUP will NOT re-read any *.conf files
|
||||
svc="killall -s SIGHUP dnsmasq"
|
||||
else
|
||||
# Get PID of dnsmasq to determine if it needs to start or restart
|
||||
if pidof dnsmasq &> /dev/null; then
|
||||
svcOption="restart"
|
||||
else
|
||||
svcOption="start"
|
||||
fi
|
||||
svc="service dnsmasq ${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}"
|
||||
else
|
||||
[[ ! -t 1 ]] && local OVER=""
|
||||
echo -e "${OVER} ${CROSS} ${output}"
|
||||
fi
|
||||
|
||||
# Send signal to FTL to have it re-parse the gravity files
|
||||
killall -s SIGHUP pihole-FTL
|
||||
}
|
||||
|
||||
piholeEnable() {
|
||||
if [[ "${2}" == "-h" ]] || [[ "${2}" == "--help" ]]; then
|
||||
echo "Usage: pihole disable [time]
|
||||
Example: 'pihole disable', or 'pihole disable 5m'
|
||||
Disable Pi-hole subsystems
|
||||
|
||||
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"
|
||||
fi
|
||||
if [[ $# > 1 ]]; then
|
||||
local error=false
|
||||
if [[ "${2}" == *"s" ]]; then
|
||||
tt=${2%"s"}
|
||||
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"}
|
||||
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 -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"
|
||||
fi
|
||||
else
|
||||
# Enable Pi-hole
|
||||
echo -e " ${INFO} Enabling blocking"
|
||||
local str="Pi-hole 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"
|
||||
fi
|
||||
fi
|
||||
|
||||
restartDNS
|
||||
|
||||
echo -e "${OVER} ${TICK} ${str}"
|
||||
}
|
||||
|
||||
piholeLogging() {
|
||||
shift
|
||||
if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then
|
||||
echo "Usage: pihole logging [options]
|
||||
Example: 'pihole logging on'
|
||||
Specify whether the Pi-hole log should be used
|
||||
|
||||
Options:
|
||||
on Enable 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
|
||||
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 -e " ${INFO} Enabling logging..."
|
||||
local str="Logging has been enabled!"
|
||||
else
|
||||
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}"
|
||||
}
|
||||
|
||||
statusFunc() {
|
||||
local addnConfigs
|
||||
|
||||
# Determine if service is running on port 53 (Cr: https://superuser.com/a/806331)
|
||||
if (echo > /dev/tcp/localhost/53) >/dev/null 2>&1; then
|
||||
if [[ "${1}" != "web" ]]; then
|
||||
echo -e " ${TICK} DNS service is running"
|
||||
fi
|
||||
else
|
||||
case "${1}" in
|
||||
"web") echo "-1";;
|
||||
*) echo -e " ${CROSS} DNS service is NOT running";;
|
||||
esac
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Determine if Pi-hole's addn-hosts configs are commented out
|
||||
addnConfigs=$(grep -i "addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)
|
||||
|
||||
if [[ "${addnConfigs}" =~ "#" ]]; then
|
||||
# A config is commented out
|
||||
case "${1}" in
|
||||
"web") echo 0;;
|
||||
*) echo -e " ${CROSS} Pi-hole blocking is Disabled";;
|
||||
esac
|
||||
elif [[ -n "${addnConfigs}" ]]; then
|
||||
# Configs are set
|
||||
case "${1}" in
|
||||
"web") echo 1;;
|
||||
*) echo -e " ${TICK} Pi-hole blocking is Enabled";;
|
||||
esac
|
||||
else
|
||||
# No configs were found
|
||||
case "${1}" in
|
||||
"web") echo 99;;
|
||||
*) echo -e " ${INFO} No hosts file linked to dnsmasq, adding it in enabled state";;
|
||||
esac
|
||||
# Add addn-host= to dnsmasq
|
||||
echo "addn-hosts=/etc/pihole/gravity.list" >> /etc/dnsmasq.d/01-pihole.conf
|
||||
restartDNS
|
||||
fi
|
||||
}
|
||||
|
||||
tailFunc() {
|
||||
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| config ).* is (${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
|
||||
}
|
||||
|
||||
piholeCheckoutFunc() {
|
||||
if [[ "$2" == "-h" ]] || [[ "$2" == "--help" ]]; then
|
||||
echo "Usage: pihole checkout [repo] [branch]
|
||||
Example: 'pihole checkout master' or 'pihole checkout core dev'
|
||||
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
|
||||
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"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
source "${PI_HOLE_SCRIPT_DIR}"/piholeCheckout.sh
|
||||
shift
|
||||
checkout "$@"
|
||||
}
|
||||
|
||||
tricorderFunc() {
|
||||
if [[ ! -p "/dev/stdin" ]]; then
|
||||
echo -e " ${INFO} Please do not call Tricorder directly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
if command -v openssl &> /dev/null; then
|
||||
openssl s_client -quiet -connect tricorder.pi-hole.net:9998 2> /dev/null < /dev/stdin
|
||||
exit "$?"
|
||||
else
|
||||
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 "."
|
||||
sleep 1
|
||||
: $((secs--))
|
||||
done
|
||||
echo " "
|
||||
nc tricorder.pi-hole.net 9999 < /dev/stdin
|
||||
exit "$?"
|
||||
fi
|
||||
}
|
||||
|
||||
updateCheckFunc() {
|
||||
"${PI_HOLE_SCRIPT_DIR}"/updatecheck.sh "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
helpFunc() {
|
||||
echo "::: Control all PiHole specific functions!"
|
||||
echo ":::"
|
||||
echo "::: Usage: pihole [options]"
|
||||
echo "::: Add -h after -w (whitelist), -b (blacklist), or -c (chronometer) for more information on usage"
|
||||
echo ":::"
|
||||
echo "::: Options:"
|
||||
echo "::: -w, whitelist Whitelist domains"
|
||||
echo "::: -b, blacklist Blacklist domains"
|
||||
echo "::: -d, debug Start a debugging session if having trouble"
|
||||
echo "::: -f, flush Flush the pihole.log file"
|
||||
echo "::: -up, updatePihole Update Pi-hole"
|
||||
echo "::: -g, updateGravity Update the list of ad-serving domains"
|
||||
echo "::: -s, setupLCD Automatically configures the Pi to use the 2.8 LCD screen to display stats on it"
|
||||
echo "::: -c, chronometer Calculates stats and displays to an LCD"
|
||||
echo "::: -h, help Show this help dialog"
|
||||
echo "::: -v, version Show current versions"
|
||||
echo "::: -q, query Query the adlists for a specific domain"
|
||||
echo "::: uninstall Uninstall Pi-Hole from your system :(!"
|
||||
exit 0
|
||||
echo "Usage: pihole [options]
|
||||
Example: 'pihole -w -h'
|
||||
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
|
||||
Add '-h' for more info on whitelist/blacklist usage
|
||||
|
||||
Debugging Options:
|
||||
-d, debug Start a debugging session
|
||||
Add '-a' to enable automated debugging
|
||||
-f, flush Flush the Pi-hole log
|
||||
-r, reconfigure Reconfigure or Repair Pi-hole subsystems
|
||||
-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
|
||||
-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
|
||||
-h, --help, help Show this help dialog
|
||||
-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 '-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 '-h' for more info on version usage
|
||||
uninstall Uninstall Pi-hole from your system
|
||||
status Display the running status of Pi-hole subsystems
|
||||
enable Enable Pi-hole subsystems
|
||||
disable Disable Pi-hole subsystems
|
||||
Add '-h' for more info on disable usage
|
||||
restartdns Restart Pi-hole subsystems
|
||||
checkout Switch Pi-hole subsystems to a different Github branch
|
||||
Add '-h' for more info on checkout usage";
|
||||
exit 0
|
||||
}
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
helpFunc
|
||||
helpFunc
|
||||
fi
|
||||
|
||||
# Handle redirecting to specific functions based on arguments
|
||||
case "$1" in
|
||||
"-w" | "whitelist" ) whitelistFunc "$@";;
|
||||
"-b" | "blacklist" ) blacklistFunc "$@";;
|
||||
"-d" | "debug" ) debugFunc;;
|
||||
"-f" | "flush" ) flushFunc;;
|
||||
"-up" | "updatePihole" ) updatePiholeFunc;;
|
||||
"-r" | "reconfigure" ) reconfigurePiholeFunc;;
|
||||
"-g" | "updateGravity" ) updateGravityFunc "$@";;
|
||||
"-s" | "setupLCD" ) setupLCDFunction;;
|
||||
"-c" | "chronometer" ) chronometerFunc "$@";;
|
||||
"-h" | "help" ) helpFunc;;
|
||||
"-v" | "version" ) versionFunc;;
|
||||
"-q" | "query" ) queryFunc "$@";;
|
||||
"uninstall" ) uninstallFunc;;
|
||||
* ) helpFunc;;
|
||||
case "${1}" in
|
||||
"-w" | "whitelist" ) whitelistFunc "$@";;
|
||||
"-b" | "blacklist" ) blacklistFunc "$@";;
|
||||
"-wild" | "wildcard" ) wildcardFunc "$@";;
|
||||
"-d" | "debug" ) debugFunc "$@";;
|
||||
"-f" | "flush" ) flushFunc "$@";;
|
||||
"-up" | "updatePihole" ) updatePiholeFunc;;
|
||||
"-r" | "reconfigure" ) reconfigurePiholeFunc;;
|
||||
"-g" | "updateGravity" ) updateGravityFunc "$@";;
|
||||
"-c" | "chronometer" ) chronometerFunc "$@";;
|
||||
"-h" | "help" ) helpFunc;;
|
||||
"-v" | "version" ) versionFunc "$@";;
|
||||
"-q" | "query" ) queryFunc "$@";;
|
||||
"-l" | "logging" ) piholeLogging "$@";;
|
||||
"uninstall" ) uninstallFunc;;
|
||||
"enable" ) piholeEnable 1;;
|
||||
"disable" ) piholeEnable 0 "$2";;
|
||||
"status" ) statusFunc "$2";;
|
||||
"restartdns" ) restartDNS "$2";;
|
||||
"-a" | "admin" ) webpageFunc "$@";;
|
||||
"-t" | "tail" ) tailFunc;;
|
||||
"checkout" ) piholeCheckoutFunc "$@";;
|
||||
"tricorder" ) tricorderFunc;;
|
||||
"updatechecker" ) updateCheckFunc "$@";;
|
||||
* ) helpFunc;;
|
||||
esac
|
||||
|
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
docker-compose
|
||||
pytest
|
||||
pytest-xdist
|
||||
pytest-cov
|
||||
testinfra
|
0
test/__init__.py
Normal file
0
test/__init__.py
Normal file
16
test/centos.Dockerfile
Normal file
16
test/centos.Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM centos:7
|
||||
|
||||
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 && \
|
61
test/conftest.py
Normal file
61
test/conftest.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import pytest
|
||||
import testinfra
|
||||
|
||||
check_output = testinfra.get_backend(
|
||||
"local://"
|
||||
).get_module("Command").check_output
|
||||
|
||||
@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 '''
|
||||
def run_bash(self, command, *args, **kwargs):
|
||||
cmd = self.get_command(command, *args)
|
||||
if self.user is not None:
|
||||
out = self.run_local(
|
||||
"docker exec -u %s %s /bin/bash -c %s",
|
||||
self.user, self.name, cmd)
|
||||
else:
|
||||
out = self.run_local(
|
||||
"docker exec %s /bin/bash -c %s", self.name, cmd)
|
||||
out.command = self.encode(cmd)
|
||||
return out
|
||||
|
||||
funcType = type(Docker.run)
|
||||
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 '''
|
||||
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)
|
||||
|
||||
def teardown():
|
||||
check_output("docker rm -f %s", docker_id)
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
docker_container = testinfra.get_backend("docker://" + docker_id)
|
||||
docker_container.id = docker_id
|
||||
return docker_container
|
||||
|
||||
@pytest.fixture
|
||||
def args(request):
|
||||
''' -t became required when tput began being used '''
|
||||
return '-t -d'
|
||||
|
||||
@pytest.fixture(params=['debian', 'centos'])
|
||||
def tag(request):
|
||||
''' consumed by image to make the test matrix '''
|
||||
return request.param
|
||||
|
||||
@pytest.fixture()
|
||||
def image(request, tag):
|
||||
''' 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 '''
|
||||
return 'tail -f /dev/null'
|
16
test/debian.Dockerfile
Normal file
16
test/debian.Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM buildpack-deps:jessie-scm
|
||||
|
||||
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 && \
|
18
test/test_000_build_containers.py
Normal file
18
test/test_000_build_containers.py
Normal file
@@ -0,0 +1,18 @@
|
||||
''' This file starts with 000 to make it run first '''
|
||||
import pytest
|
||||
import testinfra
|
||||
|
||||
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' ),
|
||||
])
|
||||
def test_build_pihole_image(image, tag):
|
||||
build_cmd = run_local('docker build -f {} -t {} .'.format(image, tag))
|
||||
if build_cmd.rc != 0:
|
||||
print build_cmd.stdout
|
||||
print build_cmd.stderr
|
||||
assert build_cmd.rc == 0
|
438
test/test_automated_install.py
Normal file
438
test/test_automated_install.py
Normal file
@@ -0,0 +1,438 @@
|
||||
import pytest
|
||||
from textwrap import dedent
|
||||
|
||||
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")
|
||||
|
||||
def test_setupVars_are_sourced_to_global_scope(Pihole):
|
||||
''' 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 '''
|
||||
setup_var_file = 'cat <<EOF> /etc/pihole/setupVars.conf\n'
|
||||
for k,v in SETUPVARS.iteritems():
|
||||
setup_var_file += "{}={}\n".format(k, v)
|
||||
setup_var_file += "EOF\n"
|
||||
Pihole.run(setup_var_file)
|
||||
|
||||
script = dedent('''\
|
||||
set -e
|
||||
printSetupVars() {
|
||||
# Currently debug test function only
|
||||
echo "Outputting sourced variables"
|
||||
echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}"
|
||||
echo "IPV4_ADDRESS=${IPV4_ADDRESS}"
|
||||
echo "IPV6_ADDRESS=${IPV6_ADDRESS}"
|
||||
echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}"
|
||||
echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}"
|
||||
}
|
||||
update_dialogs() {
|
||||
. /etc/pihole/setupVars.conf
|
||||
}
|
||||
update_dialogs
|
||||
printSetupVars
|
||||
''')
|
||||
|
||||
output = run_script(Pihole, script).stdout
|
||||
|
||||
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():
|
||||
set_setup_vars += " {}={}\n".format(k, v)
|
||||
Pihole.run(set_setup_vars).stdout
|
||||
|
||||
script = dedent('''\
|
||||
set -e
|
||||
echo start
|
||||
TERM=xterm
|
||||
source /opt/pihole/basic-install.sh
|
||||
{}
|
||||
mkdir -p /etc/dnsmasq.d
|
||||
version_check_dnsmasq
|
||||
finalExports
|
||||
cat /etc/pihole/setupVars.conf
|
||||
'''.format(set_setup_vars))
|
||||
|
||||
output = run_script(Pihole, script).stdout
|
||||
|
||||
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 '''
|
||||
# firewallD returns 'running' as status
|
||||
mock_command('firewall-cmd', {'*':('running', 0)}, Pihole)
|
||||
# Whiptail dialog returns Ok for user prompt
|
||||
mock_command('whiptail', {'*':('', 0)}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
''')
|
||||
expected_stdout = 'Configuring FirewallD for httpd and dnsmasq'
|
||||
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 --reload' in firewall_calls
|
||||
|
||||
def test_configureFirewall_firewalld_disabled_no_errors(Pihole):
|
||||
''' confirms firewalld rules are not applied when firewallD is not running '''
|
||||
# firewallD returns non-running status
|
||||
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'
|
||||
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 '''
|
||||
# firewallD returns running status
|
||||
mock_command('firewall-cmd', {'*':('running', 0)}, Pihole)
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*':('', 1)}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
''')
|
||||
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('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
''')
|
||||
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 '''
|
||||
# iptables command exists
|
||||
mock_command('iptables', {'*':('', '0')}, Pihole)
|
||||
# modinfo returns always true (ip_tables module check)
|
||||
mock_command('modinfo', {'*':('', '0')}, Pihole)
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*':('', '1')}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
''')
|
||||
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)
|
||||
# modinfo returns always true (ip_tables module check)
|
||||
mock_command('modinfo', {'*':('', '0')}, Pihole)
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*':('', '0')}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
''')
|
||||
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
|
||||
|
||||
def test_configureFirewall_IPTables_enabled_not_exist_no_errors(Pihole):
|
||||
''' 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)
|
||||
# modinfo returns always true (ip_tables module check)
|
||||
mock_command('modinfo', {'*':('', '0')}, Pihole)
|
||||
# Whiptail dialog returns Cancel for user prompt
|
||||
mock_command('whiptail', {'*':('', '0')}, Pihole)
|
||||
configureFirewall = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
configureFirewall
|
||||
''')
|
||||
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
|
||||
|
||||
def test_installPiholeWeb_fresh_install_no_errors(Pihole):
|
||||
''' 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 info_box + ' Installing blocking page...' in installWeb.stdout
|
||||
assert tick_box + ' Creating directory for blocking page, and copying files' in installWeb.stdout
|
||||
assert cross_box + ' Backing up index.lighttpd.html' in installWeb.stdout
|
||||
assert 'No default index.lighttpd.html file found... not backing up' in installWeb.stdout
|
||||
assert tick_box + ' Installing sudoer file' in installWeb.stdout
|
||||
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
|
||||
assert 'index.php' 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'''
|
||||
updateCache = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
distro_check
|
||||
update_package_cache
|
||||
''')
|
||||
assert tick_box + ' Update local cache of available packages' in updateCache.stdout
|
||||
assert 'Error: Unable to update package cache.' not in updateCache.stdout
|
||||
|
||||
def test_update_package_cache_failure_no_errors(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 cross_box + ' Update local cache of available packages' 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 '''
|
||||
# mock uname to return aarch64 platform
|
||||
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)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
''')
|
||||
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 '''
|
||||
# mock uname to return armv6l platform
|
||||
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)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
''')
|
||||
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 '''
|
||||
# mock uname to return armv7l platform
|
||||
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)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
''')
|
||||
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 '''
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
''')
|
||||
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)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
''')
|
||||
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 '''
|
||||
# mock uname to return generic platform
|
||||
download_binary = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLinstall pihole-FTL-aarch64-linux-gnu
|
||||
''')
|
||||
expected_stdout = tick_box + ' Downloading and Installing FTL'
|
||||
assert expected_stdout in download_binary.stdout
|
||||
error = 'Error: Download of binary from Github failed'
|
||||
assert error not in download_binary.stdout
|
||||
error = 'Error: URL not found'
|
||||
assert error not in download_binary.stdout
|
||||
|
||||
def test_FTL_download_unknown_fails_no_errors(Pihole):
|
||||
''' 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 = cross_box + ' Downloading and Installing FTL'
|
||||
assert expected_stdout in download_binary.stdout
|
||||
error = 'Error: URL not found'
|
||||
assert error in download_binary.stdout
|
||||
|
||||
def test_FTL_binary_installed_and_responsive_no_errors(Pihole):
|
||||
''' confirms FTL binary is copied and functional in installed location '''
|
||||
installed_binary = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
FTLdetect
|
||||
pihole-FTL version
|
||||
''')
|
||||
expected_stdout = 'v'
|
||||
assert expected_stdout in installed_binary.stdout
|
||||
|
||||
# def test_FTL_support_files_installed(Pihole):
|
||||
# ''' confirms FTL support files are installed '''
|
||||
# support_files = Pihole.run('''
|
||||
# source /opt/pihole/basic-install.sh
|
||||
# FTLdetect
|
||||
# stat -c '%a %n' /var/log/pihole-FTL.log
|
||||
# stat -c '%a %n' /run/pihole-FTL.port
|
||||
# stat -c '%a %n' /run/pihole-FTL.pid
|
||||
# ls -lac /run
|
||||
# ''')
|
||||
# assert '644 /run/pihole-FTL.port' in support_files.stdout
|
||||
# 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 '''
|
||||
# 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)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
useIPv6dialog
|
||||
''')
|
||||
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 '''
|
||||
# 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)
|
||||
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
|
||||
|
||||
def test_IPv6_only_GUA(Pihole):
|
||||
''' 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)
|
||||
detectPlatform = Pihole.run('''
|
||||
source /opt/pihole/basic-install.sh
|
||||
useIPv6dialog
|
||||
''')
|
||||
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 '''
|
||||
# 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)
|
||||
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
|
||||
|
||||
def test_IPv6_ULA_GUA_test(Pihole):
|
||||
''' 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)
|
||||
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
|
13
test/test_shellcheck.py
Normal file
13
test/test_shellcheck.py
Normal file
@@ -0,0 +1,13 @@
|
||||
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' | while read file; do shellcheck -x \"$file\" -e SC1090,SC1091; done;"
|
||||
results = run_local(shellcheck)
|
||||
print results.stdout
|
||||
assert '' == results.stdout
|
Reference in New Issue
Block a user