Compare commits
1040 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c099a573f7 | |||
| 7d1988bba1 | |||
| 492823e401 | |||
| 7250045662 | |||
| 97bda374e3 | |||
| b9bf7221ff | |||
| 0da34b3342 | |||
| ae8d59f7e1 | |||
| 6f965389c8 | |||
| 570c34049c | |||
| 4fa4ac5893 | |||
| d3fb1d590c | |||
| eb77e26c6c | |||
| 1330befd13 | |||
| 1dfaf47d12 | |||
| b2473181fd | |||
| 27f87819f1 | |||
| 4ef60d55ca | |||
| 8dcbcfc6cb | |||
| 709ceef2b7 | |||
| 3ea7d987d1 | |||
| 5decffdf7a | |||
| 0b7710a11e | |||
| 13785ab502 | |||
| 35286ddb8b | |||
| 703224d891 | |||
| 3cdcd29ef4 | |||
| 14086bba3d | |||
| 26843e4b4b | |||
| 3eee8114dd | |||
| ff53e120f4 | |||
| 2fdbd025c1 | |||
| f916916946 | |||
| acb73d36bf | |||
| ddd81702ab | |||
| baae3bfaac | |||
| 4e20cfe7a5 | |||
| 1ecd4232bc | |||
| e49138381d | |||
| fe21d10c98 | |||
| 25b18ebabc | |||
| 8fe18c9617 | |||
| b97e51fe65 | |||
| 41dba79e56 | |||
| 3f090ee968 | |||
| 5bc29b1e6e | |||
| 952f074cdd | |||
| a72fce184c | |||
| 1a38d91cf3 | |||
| 64d785ca11 | |||
| 86de552ec6 | |||
| 40cb37dfd0 | |||
| 22bf150de8 | |||
| 903a47edf7 | |||
|
|
4c6b8b564a | ||
| 57eecdeb9f | |||
| c895b45572 | |||
| 0fd96c0074 | |||
| 25bb433014 | |||
| d80de4ac65 | |||
| 1ce6fddbaf | |||
| 9d10d89b18 | |||
| 8a23c626dd | |||
| 1f38be9aea | |||
| 9b5c453552 | |||
| ab5766ff4f | |||
| f0b2b5f0ee | |||
| a48b4a7b38 | |||
| 7c9f87c224 | |||
| 1beab4b4b4 | |||
| dca6aa7154 | |||
| cfd502245c | |||
| eb07a93e20 | |||
| 1ede6ad93b | |||
| a620d0f1ca | |||
| cc5407242b | |||
| 997d039569 | |||
| c3edfae2b1 | |||
| 146b3304aa | |||
| 8cb9e6c840 | |||
| eacbb885b3 | |||
| f6fa922bb3 | |||
| 9ec0a249dd | |||
| ce1a4ed723 | |||
| f216ba338a | |||
| 657cc2e794 | |||
| f9da0c04ec | |||
| f85940955a | |||
| 945338ca01 | |||
| 2e8d46fa26 | |||
| e1a29e4e38 | |||
| 70bd04b999 | |||
| 10eb638221 | |||
| fea809b199 | |||
| 40aee20d23 | |||
| 456dce5685 | |||
| 33fe7b3b9e | |||
| 593cb405c5 | |||
| 3acb8ab47b | |||
| 4faa1d4a02 | |||
| a07fbd7cba | |||
| 7c48d5e2ed | |||
| 6dde471bf5 | |||
| 38fed7756f | |||
| 3747434d62 | |||
| 0af8be2c47 | |||
| 9fe43a2e02 | |||
| d567acd930 | |||
| b107b413df | |||
| 67d448e0a0 | |||
| 96f55fd992 | |||
| 7156578124 | |||
| ea4942bbc4 | |||
| 0bda5c7a1c | |||
| aab821d93c | |||
| b3e7b64116 | |||
| 5e6461c27f | |||
| 3061475992 | |||
| 5bae674ed9 | |||
| 939ea79a15 | |||
| 998a1e98f5 | |||
| c246fb5fdf | |||
| a3400d76f4 | |||
| 0bcdde0ffb | |||
| 9b51bcbdc3 | |||
| 7912958399 | |||
| 550731e9e9 | |||
| 81b0dad1d1 | |||
| 375fc3fcd0 | |||
| d545152d67 | |||
| 676666394b | |||
| 9dc900c75e | |||
| aea1373df7 | |||
| 9953f3978c | |||
| b304475011 | |||
| 127b1bef52 | |||
| e9f210567c | |||
| 16281d4ce1 | |||
| ee96dc087a | |||
| 67fff6d1a5 | |||
| dfecfa637d | |||
| f289ce21c7 | |||
| cf068826f3 | |||
| 7d51112571 | |||
| 93bfa04917 | |||
| 191549f88f | |||
| 8719f18f47 | |||
| a40c862bcf | |||
| 352d784eb5 | |||
| a9b57ad3ad | |||
| 870678c04b | |||
| ab2a7fd126 | |||
| c729a20ce5 | |||
| ee5f8ff8e1 | |||
| 58925093e3 | |||
| 893f2bc31a | |||
| e963b2afa6 | |||
| 813157fd17 | |||
| aabe4a1944 | |||
| 4756064405 | |||
| 5048f679ee | |||
| 51a3731124 | |||
| 847b7587a7 | |||
| f753479708 | |||
| 70bb666fa1 | |||
| 2aa95fd8db | |||
| 7144ab0106 | |||
| e60c52e6ba | |||
| ba158c9e16 | |||
| d5c0869949 | |||
| b3a8bbcc36 | |||
| 5f5a68095c | |||
| 95c16106fc | |||
| 5b69eec2f2 | |||
| 9686dca0f6 | |||
| 60168e14ae | |||
| 2431e2e00e | |||
| aa3dc51740 | |||
| 153bc8d8d6 | |||
| 84064c381a | |||
| 43d951438a | |||
| bcc0ad6bdc | |||
| b607905dab | |||
| c48db1b088 | |||
| 6d7c489b37 | |||
| c690e59d27 | |||
| d846c544d0 | |||
| 78ef6b3e76 | |||
| 0d93da4551 | |||
| 5c6651f63d | |||
| 46f30907f8 | |||
| 873be60ec6 | |||
| a76525307a | |||
| 916250bffa | |||
| 7ba17e43f7 | |||
| 30eee99f5a | |||
| 7c72e35644 | |||
| 084dbbaada | |||
| a91f47b226 | |||
| 0c27ba1e87 | |||
| f993050120 | |||
| 54b15da2e6 | |||
| 02cde33f5d | |||
| 26022f6011 | |||
| d733790c1a | |||
| 5afb45dbb3 | |||
| 7512f5f2a1 | |||
| 03ade0ea9b | |||
| ed44e675af | |||
| c2bb41351b | |||
| a3a5851745 | |||
| f1e29cd0c8 | |||
| f0d0ea8164 | |||
| 5670591035 | |||
| b91030fde4 | |||
| 3dbb5431dc | |||
| 579ae52a68 | |||
| 6da14beabe | |||
| 7c8e0f7c5a | |||
| 9b53a6d79d | |||
| 5cbe23166a | |||
| 838df9d2cc | |||
| fa35021ab3 | |||
| 39864daf0a | |||
| 4dffb1046b | |||
| a10ec04a3b | |||
| a97bcf8caa | |||
| 7834de599a | |||
| a3d14012bb | |||
| 3cdde46eda | |||
| bd7bfdb6bb | |||
| da1222f9cb | |||
| 3bbbccc1ca | |||
| c6ff30722d | |||
| 9bcb0bdb32 | |||
| 6320533f86 | |||
| 54f6b8f9ae | |||
| 7162c8e660 | |||
| 6e90f79be5 | |||
| bb529f9313 | |||
| fc31c33354 | |||
| 49417e4148 | |||
| 0dfe28559f | |||
| 4b269cc120 | |||
| 8c67830345 | |||
| 7ffbcc83fa | |||
| d039fb962d | |||
| cc4b485a24 | |||
| 9159f0e1ab | |||
| 5bb940e9d6 | |||
| 326c38595e | |||
| 4ffd06235e | |||
| 7bcd650d25 | |||
| 7325e00097 | |||
| febaa840fa | |||
| 3b3f737ed5 | |||
| 7d02d0733b | |||
| 31e8fd5c4f | |||
| 981abbb8c2 | |||
| a22181712b | |||
| 00b9daf41b | |||
| b398ba32ac | |||
| a70ee2e830 | |||
| 480410e2e7 | |||
| a1102cc97d | |||
| 96451f640c | |||
| 8c4415d321 | |||
| 2ce057e394 | |||
| 5ec1bcf89c | |||
| bb9c710637 | |||
| 16d47a12a6 | |||
| 80f9bb6d75 | |||
| f3522e42ef | |||
| 9672ad0175 | |||
| 0cadf54b4d | |||
| 487bfba2e5 | |||
| 0e4ee42f28 | |||
| abd1855bc9 | |||
| 06c7d2ca20 | |||
| 746be1d2d7 | |||
| 147c35f0c6 | |||
| b3f4a31ed2 | |||
| 4550b13565 | |||
| a8fada86fd | |||
| 7dc64fb73b | |||
| 62d8dff7c6 | |||
| dc6111b584 | |||
| 31be77be3e | |||
| a64fe1c9b0 | |||
| 4782d2d67d | |||
| 2b3d7bf5ce | |||
| 8bbb49d0b6 | |||
| 98fdb48a27 | |||
| 011d96089b | |||
| 40d0b302f1 | |||
| 6750b434b7 | |||
| e8a05e8088 | |||
| 5ce0da5d8a | |||
| 941af80018 | |||
| f9c60e707d | |||
| f9bdbcdea8 | |||
| 05d8762c6c | |||
| ac9a6bfc73 | |||
| ba2ba5fd98 | |||
| 2d131d0613 | |||
| f072fe07d0 | |||
| 94aee9e1d3 | |||
| 38a40c5551 | |||
| da74063d8a | |||
| 07155c9f0e | |||
| 1138347b29 | |||
| 32f46329ba | |||
| 3a62e8dcaa | |||
| af76831ca1 | |||
| 532a45533e | |||
| 6a6a61cb78 | |||
| 17a644f05d | |||
| dc9565185c | |||
| c608ed3af3 | |||
| fb4ddb5cbe | |||
| 8d825b561c | |||
| d15083553e | |||
| ef2919899b | |||
| 3f48cc4e46 | |||
| 3f2b7e24e6 | |||
| a0fe13c7c2 | |||
| 9f59885dbc | |||
| 1b351859b8 | |||
| ba1ff95b21 | |||
| 277fc4e02d | |||
| 551c28f242 | |||
| 9cd39c5691 | |||
| fda31a262b | |||
| 87bc6220f5 | |||
| 45ec8e5ec9 | |||
| dcd41b5084 | |||
| 1168e8b0dd | |||
| 6db1be4046 | |||
| 68418588b7 | |||
| 5e5c54293b | |||
| fed639b674 | |||
| af6119feb5 | |||
| 0ca6d17485 | |||
| c747b66455 | |||
|
|
8767a0024a | ||
|
|
a1872e6cf7 | ||
|
|
14e727ee6f | ||
| 0808c8affc | |||
| 3ebebbe5b9 | |||
| a46557020b | |||
| bd9ba3941e | |||
| 4e456f41da | |||
| 84f716b5cd | |||
| 3a2cd84d92 | |||
| 70c09a3c7b | |||
| 29f5dab4da | |||
| 40d5b147b5 | |||
| 29dc4694b4 | |||
| f18c3843ca | |||
| 2840795b78 | |||
| a8cf29c5fe | |||
| 5a320658c2 | |||
| 93fbd29a1c | |||
| aff124dd8c | |||
| 81ab4a7192 | |||
| 326ee3953d | |||
| db25a632fe | |||
| 9c8c7099e5 | |||
| b80e73a5ad | |||
| 0baea441ae | |||
| 228114f82c | |||
| 297d03b0f2 | |||
| 23f519102d | |||
| 6cece2c832 | |||
| b57f50d4b5 | |||
| eaa367a5c9 | |||
| 2a90a381a4 | |||
| da9563f870 | |||
| c90bc0ef37 | |||
| 15edb2b5c9 | |||
| c6ca5228ac | |||
| 2774e54732 | |||
| 585a24bbbe | |||
| bed7e79758 | |||
| 4b956cb488 | |||
| a42d5e06f4 | |||
| d0b8b3ad57 | |||
| af09772d67 | |||
| 8689a68c2f | |||
| 388c6302cb | |||
| 249e868c3d | |||
| a5766a8a1e | |||
| ccbc257cc0 | |||
| 86f0f14c34 | |||
| 1fcbcb4287 | |||
| b2f9d990f2 | |||
| bec6c727e2 | |||
| ad06ca0cb1 | |||
| 1107f9224c | |||
| a84a6a7f10 | |||
| 43ee4e5ae0 | |||
| 2669bd729e | |||
| 95b6bddb99 | |||
| 641695a2be | |||
| f0e15ceb81 | |||
| 2da38458db | |||
| 586e3fc61c | |||
| 4678b5f7c8 | |||
| 1e09ddc9ff | |||
| 467a7e7925 | |||
| bad27e20ad | |||
| a3ab558bb4 | |||
| bd665b7065 | |||
| f93334e2d3 | |||
| 944e4dcea5 | |||
| 8ab3c765c0 | |||
| 60e5d86f8e | |||
|
|
13f802c195 | ||
|
|
7147ef1753 | ||
|
|
080383d9a9 | ||
|
|
30f661183c | ||
|
|
70aa8e40ac | ||
|
|
207c52663b | ||
|
|
7c1a9f4bdc | ||
|
|
2b98eeabca | ||
|
|
ef426c95b5 | ||
|
|
e76ee88b01 | ||
|
|
908ffdc999 | ||
|
|
a36589b66f | ||
|
|
5faf3dbbad | ||
|
|
bc3f6946f7 | ||
|
|
fa5eaf33f9 | ||
|
|
be2407a250 | ||
|
|
f4ce98ceba | ||
|
|
2d9032f5b6 | ||
|
|
4dc5c51beb | ||
|
|
72259303b9 | ||
|
|
314675c32e | ||
|
|
ccaf2a67b2 | ||
|
|
7a9d62cdd1 | ||
| 1b7c248102 | |||
| c1970b8c23 | |||
| e8522bf2bb | |||
| 1fe4fd9849 | |||
| 869293ced9 | |||
| 8a4c2c059c | |||
| cf75348c2c | |||
| ac0efb90e5 | |||
| b9b2422c69 | |||
| 29a8731fb5 | |||
| 78a013f28f | |||
| ec88d6f747 | |||
| f7c6d5d3c3 | |||
| fd8f46be13 | |||
| 701d647a91 | |||
| 8643ec2c7c | |||
| ee58fc1fb9 | |||
| 5f9aec8233 | |||
| 0348d708d9 | |||
| e09216012f | |||
| daffb606ee | |||
| df29f8af6b | |||
| 5189326431 | |||
| 35d512262d | |||
| 53b0d9275b | |||
| 5fbd33e845 | |||
| fa96f6ea13 | |||
| adce6db578 | |||
| 3265b19f3d | |||
| d3597f339e | |||
| efac71eb63 | |||
| 65284860fb | |||
| 5c02d74dca | |||
| b1bf352f11 | |||
| 12121c7078 | |||
| 749580d041 | |||
| 47b4ab6636 | |||
| b9d0527c52 | |||
| e9c0855f65 | |||
| c1f2f0b6d8 | |||
| 426d77147f | |||
| e301bff2c2 | |||
| 9c12dacfb2 | |||
| 96b755be49 | |||
| fef8323282 | |||
| a573a7e1c3 | |||
| 28c1d61d20 | |||
| 177a7fbec2 | |||
| 2f2bfacbb7 | |||
| ceee0495eb | |||
| 7e69eff7a8 | |||
| a81e6f80e6 | |||
| e438d3aec7 | |||
| b4e07a0d88 | |||
| dcd1525c4f | |||
| e9e19053f4 | |||
| df2a7c03c1 | |||
| 752944f741 | |||
| 8932242a86 | |||
| 0f05eeba29 | |||
| 3c459134da | |||
| 77a023d777 | |||
| ac2e5a4fcf | |||
| 9150c8b784 | |||
| d3293526f4 | |||
| bb0a74b98c | |||
| 6f44a37cd0 | |||
| 9af378ed7c | |||
| 4dcebdfbf3 | |||
| 5c84d3f778 | |||
| c515033bfa | |||
| 63adbc6b3a | |||
| 3c5f9c1bbe | |||
| c3500ee324 | |||
| d1b37eddc1 | |||
| 095c00c6ee | |||
| 4b7e34eb17 | |||
| b1a68e20d7 | |||
| a71d56e473 | |||
| 58d8722ae2 | |||
| c75a67d34b | |||
| 364f981a58 | |||
| 5d5da09c0d | |||
| 0a4f87b0ce | |||
| 8c4ed4ffc5 | |||
| 214e2c345d | |||
| a41271e296 | |||
| 990618bfeb | |||
| f2373edd8e | |||
| 8b25326b2d | |||
| 4aca178f85 | |||
| d69b0051e1 | |||
| b1739b02a0 | |||
| 44cc931840 | |||
| 7e2db4c1ee | |||
| 11401513e2 | |||
| b9e99ac4d8 | |||
| 7f9f551b67 | |||
| 3fdf714e90 | |||
| 2eaf7b0230 | |||
| 21e15272e4 | |||
| f5bee18d19 | |||
| 54d1e11eeb | |||
| c63f17192e | |||
| ae475a04f2 | |||
| 54654a73bd | |||
| 638796cc44 | |||
| 21a71d0e67 | |||
| 825a893440 | |||
| 462ae7d4b9 | |||
| d2561eb43e | |||
| 2a334992ab | |||
| 578818271f | |||
| b4fd86f4e2 | |||
| 60dcc15536 | |||
| e526a6736f | |||
| 3f467f6f1c | |||
| 1d2ebf254e | |||
| a732c3a9ff | |||
| 90f0fb58d9 | |||
| 8cb599ee60 | |||
| f9a0a11ee8 | |||
| e614e08d0c | |||
| f5d806a51a | |||
| 8c6fcfe99c | |||
| 0077240255 | |||
| a868d28452 | |||
| d397cc22ad | |||
| 772b807c3d | |||
| 6e9de7f2fa | |||
| 112800c98b | |||
| 20d054245d | |||
| cbf37d8ea1 | |||
| 1aba3476da | |||
| 8f792ac76d | |||
| 96c806198a | |||
| 9dc21dd1d2 | |||
| 3611ff920a | |||
| 4c3be6eac6 | |||
| 519572b53a | |||
| 644b709f6f | |||
| 48a3eb461d | |||
| ae488138e0 | |||
| 61f2ab5777 | |||
| 2ff8690a66 | |||
| 3d4fc4c3f3 | |||
| 5784a3c6da | |||
| e99f16300f | |||
| ed339866f7 | |||
| f70cf16040 | |||
| 55bc3f250d | |||
| 29b54a7b85 | |||
| 8c3b6865e6 | |||
| 4f897d5a9c | |||
| 7aece55434 | |||
| 2b2d649a9f | |||
| 4912b17bf3 | |||
| 9c80c357d6 | |||
| 9b64aa3939 | |||
| 40e1f78216 | |||
| cd8bd6c8fb | |||
| da0cecbd8b | |||
| ff9e37b4e5 | |||
| d99f0e0179 | |||
| e7470b6fc5 | |||
| bfcf0e705b | |||
| 96c1251ae2 | |||
| 43328ab3a1 | |||
| a61d8bd666 | |||
| ddbe3b0e71 | |||
| f395f7aec1 | |||
| ff12a7e612 | |||
| 9318e97400 | |||
| 16f0b1dc2a | |||
| cb0275dc9d | |||
| bd77216ab7 | |||
| 110acfc6e2 | |||
| 3d9030f064 | |||
| 2fc503773c | |||
| 17ee37d671 | |||
| 84cdc51a77 | |||
| ee8beb194e | |||
| ac1f02c3a0 | |||
| 385f4a1d15 | |||
| d06f505a84 | |||
| 7b3fd34e63 | |||
| f2adf6f5c0 | |||
| 73ca03da8e | |||
| 496b28c8a6 | |||
| 4228c9a3a9 | |||
| 26fa73a40b | |||
| 2d4a104882 | |||
| 54cb77f737 | |||
| eb4e9914d4 | |||
| a0f9d0afed | |||
| 867df93c30 | |||
| ce275d86a4 | |||
| 6b159cc6d4 | |||
| f879c97e53 | |||
| eed455e3bb | |||
| 6a5bba5ed3 | |||
| 1f8b795e9a | |||
| d1f8307153 | |||
| 0182777f56 | |||
| 7b7e50b524 | |||
| ab4c201b4b | |||
| f452216035 | |||
| df33152a38 | |||
| fa39c85fe4 | |||
| 51b6bd0fdf | |||
| 6fe273cf8b | |||
| 3088a2452a | |||
| 3c6341953d | |||
| 112cfb3a4b | |||
| 54cfa39b77 | |||
| 1269d1fa87 | |||
| 2de8b0674e | |||
| de38c6f2e4 | |||
| e8bc2e1005 | |||
| 971c8c66ad | |||
| 8ad9e834ef | |||
| e869fc96b0 | |||
| c10e3f4fd0 | |||
| 1134477c49 | |||
| 8e7083df6b | |||
| 3f9ad7842d | |||
| 20cfe6a81d | |||
| 2ffa16248f | |||
| 8ea142cfdf | |||
| 1e0d7f7ef8 | |||
| b3f8790f01 | |||
| a953d81b96 | |||
| 1b67a4293d | |||
| c1005f6212 | |||
| 96a74f956b | |||
| ef0b2cbb58 | |||
| 9fc882533d | |||
| d2ee7e4d0e | |||
| 823de2dd9e | |||
| 6e53e7c7d2 | |||
| e64b7f4eaa | |||
| c30e30232f | |||
| f5f4c79326 | |||
| 70995f5dcf | |||
| f17da656bb | |||
| a9c874d79e | |||
| 3bb2100dcd | |||
| f0abf176e8 | |||
| cd9e89485e | |||
| f0e5b3945d | |||
| a6009b9923 | |||
| a07e4a4928 | |||
| f5f6e71516 | |||
| 4985df805d | |||
| 06018125b2 | |||
| 6a1315a9f2 | |||
| aa4356c08c | |||
| e8457756fb | |||
| 97c5fc4c4d | |||
| a53050b78d | |||
| de247c919b | |||
| 9553cbcbc8 | |||
| 0101506f2c | |||
| b3b13be5a7 | |||
| ea43d901ea | |||
| baebac669e | |||
| 7b65833c20 | |||
| 07f2f338a2 | |||
| a43c705f28 | |||
| 67c5384e5b | |||
| b67a624038 | |||
| 5e75fffa86 | |||
| dd08bc79e9 | |||
| 1df64c30f8 | |||
| b28a374cdb | |||
| b63589c39e | |||
| 6bd57dd5a2 | |||
| bd08921f17 | |||
| f473d315f8 | |||
| c7309ad381 | |||
| d9e062ca7e | |||
| fe6762cf05 | |||
| 2a0a80b09a | |||
| f6fdc047cf | |||
| eb80580794 | |||
| 60bfa373e2 | |||
| a0db819a76 | |||
| a0f4d74656 | |||
| 8a6dfc5cdf | |||
| 54fe08545e | |||
| 7eaa5d73cd | |||
| 1c7677d805 | |||
| 414e3e2128 | |||
| 6a7ef8ebcb | |||
| ff7cd410f9 | |||
| e147dbc6d2 | |||
| d6de2c5b65 | |||
| c9c1007eaa | |||
| 679a0329cc | |||
| df1706a8bf | |||
| a4f57787e7 | |||
| 26199cf271 | |||
| 1e21b1bd59 | |||
| 3f96a31e1e | |||
| 569810c3ca | |||
| 6d7a5e1eae | |||
| 2218c03172 | |||
| f73a1f8191 | |||
| 1fbdb2b5e2 | |||
| 6876d1c0ef | |||
| 89a09a322e | |||
| ead4715c38 | |||
| 2bde723c43 | |||
| a7926e58a4 | |||
| f39bb8191e | |||
| e590547e3c | |||
| 5aeeb41995 | |||
| 9cad5419d8 | |||
| 4aa50a044a | |||
| d447e694b2 | |||
| 8835fb486d | |||
| c73bf2521c | |||
| 4c3fe1d66f | |||
| b6ecf348d4 | |||
| 809f338cd2 | |||
| e0c3716e08 | |||
| 62e78cdc2c | |||
| b268385e0b | |||
| c45c1c9225 | |||
| 28ee4a3ed4 | |||
| d8ce7f36e9 | |||
| 63fa4d6f80 | |||
| 9333fb8f3e | |||
| bc4fa92e16 | |||
| 054cf25811 | |||
| 85212fb171 | |||
| 8be59c2c13 | |||
| 23ffcae432 | |||
| 697580f9a4 | |||
| 3428e9dff8 | |||
| e02cf4a602 | |||
| 4245d13375 | |||
| e86b06b84d | |||
| 9579d0fced | |||
| a8ed58887d | |||
| c29e84131b | |||
| 2c87021301 | |||
| 402f3854d5 | |||
| 0cef1d28eb | |||
| a47579ab79 | |||
| 6853e7bc21 | |||
| 06b5ebe8b4 | |||
| a8536bf0d5 | |||
| 1bc77334a4 | |||
| 07a92d2a8e | |||
| eec4d01ddf | |||
| da4ae79fcc | |||
| 4d600993e2 | |||
| adda1d76ae | |||
| a419d4e808 | |||
| d246ea1d86 | |||
| 1dbbb41dd3 | |||
| 4cb158b6b2 | |||
| 7f0b53db40 | |||
| b7423ae3b5 | |||
| 957a53ad12 | |||
| 721a083c85 | |||
| d8e28ce785 | |||
| af46962fd7 | |||
| 63501bbb28 | |||
| 7944312309 | |||
| f20d8b5af3 | |||
| 6068b4390a | |||
| 931ce84d67 | |||
| 827981d178 | |||
| 8fef0a8e2f | |||
| 990037e6c1 | |||
| ceb3fc4835 | |||
| b372e19970 | |||
| 187421a291 | |||
| 2583358963 | |||
| a839140ff4 | |||
| ac259c9c37 | |||
| c4575db44c | |||
| 23a88401b7 | |||
| 1e5f72f79a | |||
| a0a76ee15d | |||
| 83e96f9135 | |||
| b1e5727d54 | |||
| 53af3859e4 | |||
| 014da1b224 | |||
| 0fcfc4b5ac | |||
| 97d0125c03 | |||
| 49c1987ccf | |||
| 88dffd7432 | |||
| dfe009c413 | |||
| 73bd9bb009 | |||
| dd34c987fb | |||
| a5f7a04528 | |||
| f3496ed795 | |||
| 816ebd4d49 | |||
| 103adc076a | |||
| 279ebcf988 | |||
| e2fcacf579 | |||
| 5a7a16588a | |||
| c40c80069f | |||
| 1e064bc9e6 | |||
| c33d1b9706 | |||
| 1d9c3b1869 | |||
| 41b8d632ae | |||
| 5cf2fa6f27 | |||
| 495e265de0 | |||
| d9ab1730b4 | |||
| b3da1390a1 | |||
| 7205ac6c35 | |||
| ff715b7a10 | |||
| 7d7cf21263 | |||
| 2c0093aa9a | |||
| 12ea0fdd53 | |||
| b3f2a86f46 | |||
| 8d6b29be53 | |||
| ad6980f82b | |||
| 57572c7527 | |||
| 2372fb0d2f | |||
| ac40f61695 | |||
| 2750440c29 | |||
| 44bde68803 | |||
| fb77214340 | |||
| 9c34c08699 | |||
| d8de2f196b | |||
|
|
598dc8020d | ||
|
|
1795833334 | ||
|
|
5798d48342 | ||
|
|
34b426ae36 | ||
|
|
dc1011ce5a | ||
|
|
cc0c9a1795 | ||
|
|
e1984a5417 | ||
|
|
41400d7710 | ||
|
|
4c500f83a5 | ||
|
|
742a944ace | ||
|
|
26e8c93282 | ||
|
|
05a9245a4c | ||
|
|
79713c700f | ||
|
|
08ded5311f | ||
|
|
e7c519f605 | ||
|
|
dab3bb5f68 | ||
|
|
a6893cda4c | ||
|
|
b544787ae6 | ||
|
|
f0179ec962 | ||
| 3216c2f46b | |||
| f8291d91c8 | |||
| 31b029c828 | |||
| 1cd326e2ea | |||
| 527049cdb2 | |||
|
|
3bb8e786e4 | ||
|
|
09994a7c73 | ||
|
|
b4acdad3f0 | ||
|
|
1c5b4bbfd8 | ||
|
|
73654b2213 | ||
|
|
93e4bd5c0f | ||
|
|
122df6240c | ||
|
|
fb9731539f | ||
|
|
4afbc1a0a4 | ||
|
|
bff141ff8d | ||
|
|
47aeed6146 | ||
|
|
8d0ad7c20d | ||
|
|
a8a2fce662 | ||
|
|
ceae05817d | ||
|
|
f9ab36fc7c | ||
|
|
8189d412eb | ||
|
|
877a9c6634 | ||
|
|
85653593df | ||
|
|
3c3eada6a7 | ||
|
|
ebc040cd56 | ||
|
|
a7fd06598d | ||
|
|
0546ac3e84 | ||
|
|
56a74798e5 | ||
|
|
8e95eb5010 | ||
|
|
f1b3ffb483 | ||
|
|
3c950e3c56 | ||
|
|
63086dcb1e | ||
|
|
b495ce399d | ||
|
|
b1f495dada | ||
|
|
f5caaf09c2 | ||
|
|
f097b98c85 | ||
|
|
e642bfd2d3 | ||
|
|
ba302f0c5b | ||
|
|
cdb0963139 | ||
|
|
5ad18a7fee | ||
|
|
e56647ed48 | ||
|
|
117a47e94c | ||
|
|
00b3c405d0 | ||
|
|
2a1cc81e46 | ||
|
|
1b5e05b81a | ||
|
|
e4410b1258 | ||
|
|
6bd29e402c | ||
|
|
e2c07d5710 | ||
|
|
5c34ebf34b | ||
|
|
7b57329dee | ||
|
|
08768424f0 | ||
|
|
904b41dbae | ||
|
|
4c0596b2df | ||
|
|
e3d62eab15 | ||
|
|
41b4a258f6 | ||
|
|
6178ae81cd | ||
|
|
80e903577e | ||
|
|
171eb0722f | ||
|
|
a643b38bbe | ||
|
|
588fa8d51a | ||
| b669b221f9 | |||
| b7dc524096 | |||
| 83984cd6ee | |||
| 6854d5bad7 | |||
| 6c37c344ce | |||
| 7e78eb5dca | |||
| 0d35d81b97 | |||
| 25d3413986 | |||
| 30141916b4 | |||
| 162712871e | |||
| 0fae4128ed | |||
| 824c184d59 | |||
| 1a007e37ad | |||
| 03736998cf | |||
| f35869b2b0 | |||
| aa1fda8ac4 | |||
| dc8f48a6b4 | |||
| 91b7149f93 | |||
| 6cf74c0361 | |||
| 66cfe2b246 | |||
| cd1017b522 | |||
| 08e8396c27 | |||
| a17c83e5f3 | |||
| 9f5a8ace72 | |||
| 79ad9f1412 | |||
| 5658b19cac | |||
| ba5c380b78 | |||
| 787e944cd1 | |||
| 251bc37fb6 | |||
| 402d73c395 | |||
| e9d65fe29b | |||
| 7506cd017b | |||
| 8d5a4b4079 | |||
| 4206afe0c3 | |||
| aec3e8208c | |||
| ba06b7d635 | |||
| d46ae18045 | |||
| 462a87b1ce | |||
| 146244d3fd | |||
| aae7c1d543 | |||
| dd13f7bd24 | |||
| 9698cfbf6d | |||
| 580d1de915 | |||
| 1d88c57be8 | |||
| 5edde45f0e | |||
| 58dead2382 | |||
| 555a661e92 | |||
| 5bb3f48d4e | |||
| 73367d2cde | |||
|
|
06ab30e909 | ||
| 92822a1458 | |||
| b6ea2855eb | |||
| 4837f266a9 | |||
| ddb40f4bc0 | |||
| 2756d57395 | |||
| 7d1850a480 | |||
| b043b63d2d | |||
| b9b7b0456a | |||
| 3875d4b2bd | |||
| c25a46b49f | |||
| 5b08a3b4b4 | |||
| 84f3a4e819 | |||
| 02a1593203 | |||
| 887cfd9f92 | |||
| 7bce6076c8 | |||
| 33d1f42002 | |||
| 2301c89b8c | |||
| 84120f1505 | |||
| 6f3a1ee55f | |||
| 9c1c8b90be | |||
| edb82df4d3 | |||
| 42c5e918b6 | |||
| 4f2f696d60 | |||
| 251c232a6e | |||
| 39f947bdd1 | |||
| 4e022ed843 | |||
| 31a3d5a43b | |||
| 1244b7f5e5 | |||
| 298222c9fc | |||
| 2369786b66 | |||
| ceba2e5ff2 | |||
| 241f5cb897 | |||
| 90a9751883 | |||
| b57374b177 | |||
| c16a540b3e | |||
| 712eeff79e | |||
| 9f77b532b6 | |||
| b42f393a1a | |||
| ed3323fffb | |||
| 71297eacc8 | |||
| 91b5dbfe88 |
|
|
@ -1,7 +0,0 @@
|
|||
Copyright (c) 2023 Alan Mason
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
BIN
images/ConEmu.jpg
Normal file
|
After Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 113 KiB |
BIN
images/Linux.jpg
Normal file
|
After Width: | Height: | Size: 333 KiB |
BIN
images/Linux.png
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 14 KiB |
BIN
images/Syslinux.jpg
Normal file
|
After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 15 KiB |
BIN
images/WinPE-Dev.jpg
Normal file
|
After Width: | Height: | Size: 407 KiB |
BIN
images/WinPE.jpg
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 378 KiB |
122
images/logo.svg
|
|
@ -1,122 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32"
|
||||
height="32"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r"
|
||||
sodipodi:docname="logo.svg"
|
||||
inkscape:export-filename="/home/thewizardpp/projects/logos/logo512x512.png"
|
||||
inkscape:export-xdpi="1440"
|
||||
inkscape:export-ydpi="1440">
|
||||
<defs
|
||||
id="defs4">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 526.18109 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective10" />
|
||||
<filter
|
||||
id="filter3668"
|
||||
inkscape:label="Drop shadow"
|
||||
width="1.5"
|
||||
height="1.5"
|
||||
x="-.25"
|
||||
y="-.25">
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur3670"
|
||||
in="SourceAlpha"
|
||||
stdDeviation="1,000000"
|
||||
result="blur" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix3672"
|
||||
result="bluralpha"
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0,500000 0 " />
|
||||
<feOffset
|
||||
id="feOffset3674"
|
||||
in="bluralpha"
|
||||
dx="1,000000"
|
||||
dy="1,000000"
|
||||
result="offsetBlur" />
|
||||
<feMerge
|
||||
id="feMerge3676">
|
||||
<feMergeNode
|
||||
id="feMergeNode3678"
|
||||
in="offsetBlur" />
|
||||
<feMergeNode
|
||||
id="feMergeNode3680"
|
||||
in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="16.469461"
|
||||
inkscape:cy="15.775995"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer2"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1152"
|
||||
inkscape:window-height="844"
|
||||
inkscape:window-x="-2"
|
||||
inkscape:window-y="93"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer"
|
||||
style="display:inline">
|
||||
<g
|
||||
id="g3613"
|
||||
transform="matrix(1.0696952,0,0,1.0696952,-1.9682871,1.2767394)">
|
||||
<path
|
||||
sodipodi:nodetypes="cssssss"
|
||||
d="m 28.466519,15.480445 c -1.690444,-0.411311 -3.880242,0.0024 -6.862802,1.703057 -4.343818,2.477 -5.647804,4.7124 -10.531132,6.5262 -2.7416801,1.0184 -7.1725478,1.2727 -6.7296333,-1.9563 0.4055207,-2.9564 4.8746766,-5.683963 10.7473903,-5.268022 7.253753,0.513753 7.780294,2.643843 11.236758,2.445771 4.073631,-0.233438 3.02577,-3.235043 2.139419,-3.450706 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="path2822"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccscsc"
|
||||
id="path2832"
|
||||
d="m 22.349625,16.595174 c -5.498466,2.959917 -4.603518,5.10607 -10.999048,3.821601 1.40216,-4.418086 4.962036,-16.95097 7.147841,-17.2692571 1.878431,-0.2735287 4.924495,4.2931483 4.924495,4.2931483 0,0 -3.661803,-2.9673231 -4.16688,-1.7046325 -0.593183,1.4829546 2.39459,8.4145833 3.093592,10.8591403 z"
|
||||
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccc"
|
||||
id="path3611"
|
||||
d="m 22.074942,15.74979 c 1.515307,-0.313608 1.831341,-0.3546 3.377477,-0.485523 1.799175,-0.173029 3.187957,0.237433 3.187957,0.237433"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
BIN
images/macOS-10.11.jpg
Normal file
|
After Width: | Height: | Size: 237 KiB |
BIN
images/macOS-10.13.jpg
Normal file
|
After Width: | Height: | Size: 756 KiB |
BIN
images/macOS-10.15.jpg
Normal file
|
After Width: | Height: | Size: 480 KiB |
BIN
images/macOS.png
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 1.6 MiB |
|
|
@ -45,6 +45,7 @@ BASE_MENUS = {
|
|||
MenuEntry('CHKDSK', 'auto_chkdsk'),
|
||||
MenuEntry('DISM RestoreHealth', 'auto_dism'),
|
||||
MenuEntry('SFC Scan', 'auto_sfc'),
|
||||
MenuEntry('Fix File Associations', 'auto_fix_file_associations'),
|
||||
MenuEntry('Clear Proxy Settings', 'auto_reset_proxy'),
|
||||
MenuEntry('Disable Pending Renames', 'auto_disable_pending_renames'),
|
||||
MenuEntry('Registry Repairs', 'auto_repair_registry'),
|
||||
|
|
@ -52,15 +53,23 @@ BASE_MENUS = {
|
|||
MenuEntry('Reset Windows Policies', 'auto_reset_windows_policies'),
|
||||
),
|
||||
'Malware Cleanup': (
|
||||
MenuEntry('Disable Defender Scans', 'auto_disable_defender', selected=False),
|
||||
MenuEntry('BleachBit', 'auto_bleachbit'),
|
||||
MenuEntry('HitmanPro', 'auto_hitmanpro'),
|
||||
MenuEntry('KVRT', 'auto_kvrt'),
|
||||
MenuEntry('Windows Defender', 'auto_microsoft_defender'),
|
||||
MenuEntry('EmsisoftCmd', 'auto_emsisoft_cmd_run', selected=False),
|
||||
MenuEntry(REBOOT_STR, 'auto_reboot', selected=False),
|
||||
MenuEntry('EmsisoftCmd (Uninstall)', 'auto_emsisoft_cmd_uninstall', selected=False),
|
||||
MenuEntry('Enable Defender Scans', 'auto_enable_defender', selected=False),
|
||||
MenuEntry('Windows Defender', 'auto_microsoft_defender', selected=False),
|
||||
MenuEntry('Remove Custom Power Plan', 'auto_remove_power_plan'),
|
||||
MenuEntry(REBOOT_STR, 'auto_reboot'),
|
||||
),
|
||||
'Manual Steps': (
|
||||
MenuEntry('Malwarebytes (Install)', 'auto_mbam_install'),
|
||||
MenuEntry(REBOOT_STR, 'auto_reboot'),
|
||||
MenuEntry('AdwCleaner', 'auto_adwcleaner'),
|
||||
MenuEntry('Malwarebytes (Run)', 'auto_mbam_run'),
|
||||
MenuEntry('Malwarebytes (Uninstall)', 'auto_mbam_uninstall'),
|
||||
MenuEntry('Bulk Crap Uninstaller', 'auto_bcuninstaller'),
|
||||
MenuEntry('Enable Windows Updates', 'auto_windows_updates_enable'),
|
||||
),
|
||||
|
|
@ -81,6 +90,112 @@ BASE_MENUS = {
|
|||
}
|
||||
PRESETS = {
|
||||
'Default': { # Will be expanded at runtime using BASE_MENUS
|
||||
'Options': (
|
||||
'Run AVRemover (once)',
|
||||
'Run RKill',
|
||||
'Sync Clock',
|
||||
),
|
||||
},
|
||||
'Data Transfer (to a clean Windows install)': {
|
||||
'Backup Settings': (
|
||||
'Enable RegBack',
|
||||
'Enable System Restore',
|
||||
'Set System Restore Size',
|
||||
'Reset Power Plans',
|
||||
'Set Custom Power Plan',
|
||||
),
|
||||
'Malware Cleanup': (
|
||||
'Disable Defender Scans',
|
||||
'BleachBit',
|
||||
'HitmanPro',
|
||||
'KVRT',
|
||||
'Enable Defender Scans',
|
||||
'Remove Custom Power Plan',
|
||||
),
|
||||
'Manual Steps': (
|
||||
'Malwarebytes (Install)',
|
||||
REBOOT_STR,
|
||||
'AdwCleaner',
|
||||
'Malwarebytes (Run)',
|
||||
'Malwarebytes (Uninstall)',
|
||||
'Enable Windows Updates',
|
||||
),
|
||||
'Options': (
|
||||
'Sync Clock',
|
||||
),
|
||||
},
|
||||
'Full Malware/Virus Cleanup': {
|
||||
'Backup Settings': (
|
||||
'Enable RegBack',
|
||||
'Enable System Restore',
|
||||
'Set System Restore Size',
|
||||
'Create System Restore',
|
||||
'Backup Browsers',
|
||||
'Backup Power Plans',
|
||||
'Reset Power Plans',
|
||||
'Set Custom Power Plan',
|
||||
'Backup Registry',
|
||||
),
|
||||
'Windows Repairs': (
|
||||
'Disable Windows Updates',
|
||||
'Reset Windows Updates',
|
||||
REBOOT_STR,
|
||||
'CHKDSK',
|
||||
'DISM RestoreHealth',
|
||||
'SFC Scan',
|
||||
'Fix File Associations',
|
||||
'Clear Proxy Settings',
|
||||
'Disable Pending Renames',
|
||||
'Registry Repairs',
|
||||
'Reset UAC',
|
||||
'Reset Windows Policies',
|
||||
),
|
||||
'Malware Cleanup': (
|
||||
'Disable Defender Scans',
|
||||
'BleachBit',
|
||||
'HitmanPro',
|
||||
'KVRT',
|
||||
'EmsisoftCmd',
|
||||
REBOOT_STR,
|
||||
'EmsisoftCmd (Uninstall)',
|
||||
'Enable Defender Scans',
|
||||
'Windows Defender',
|
||||
'Remove Custom Power Plan',
|
||||
),
|
||||
'Manual Steps': (
|
||||
'Malwarebytes (Install)',
|
||||
REBOOT_STR,
|
||||
'AdwCleaner',
|
||||
'Malwarebytes (Run)',
|
||||
'Malwarebytes (Uninstall)',
|
||||
'Bulk Crap Uninstaller',
|
||||
'Enable Windows Updates',
|
||||
),
|
||||
'Options': (
|
||||
'Run AVRemover (once)',
|
||||
'Run RKill',
|
||||
'Sync Clock',
|
||||
),
|
||||
},
|
||||
'Minimal Repairs': {
|
||||
'Backup Settings': (
|
||||
'Enable RegBack',
|
||||
'Enable System Restore',
|
||||
'Set System Restore Size',
|
||||
'Create System Restore',
|
||||
'Backup Browsers',
|
||||
'Backup Power Plans',
|
||||
'Backup Registry',
|
||||
),
|
||||
'Windows Repairs': (
|
||||
'CHKDSK',
|
||||
'DISM RestoreHealth',
|
||||
'SFC Scan',
|
||||
'Fix File Associations',
|
||||
'Clear Proxy Settings',
|
||||
'Disable Pending Renames',
|
||||
'Registry Repairs',
|
||||
),
|
||||
'Options': (
|
||||
'Run RKill',
|
||||
'Sync Clock',
|
||||
|
|
|
|||
|
|
@ -34,17 +34,24 @@ BASE_MENUS = {
|
|||
),
|
||||
'Install Software': (
|
||||
MenuEntry('Winget', 'auto_install_winget'),
|
||||
MenuEntry('ESET NOD32 AV', 'auto_install_eset_nod32_av', selected=False),
|
||||
MenuEntry('ESET NOD32 AV (MSP)', 'auto_install_eset_nod32_av_msp', selected=False),
|
||||
MenuEntry('Firefox', 'auto_install_firefox'),
|
||||
MenuEntry('LibreOffice', 'auto_install_libreoffice', selected=False),
|
||||
MenuEntry('Open Shell', 'auto_install_open_shell'),
|
||||
MenuEntry('Open-Shell', 'auto_install_open_shell', selected=False),
|
||||
MenuEntry('Software Bundle', 'auto_install_software_bundle'),
|
||||
MenuEntry('Software Upgrades', 'auto_install_software_upgrades'),
|
||||
MenuEntry('Software Upgrades', 'auto_install_software_upgrades', selected=False),
|
||||
MenuEntry('Visual C++ Runtimes', 'auto_install_vcredists'),
|
||||
),
|
||||
'Configure System': (
|
||||
MenuEntry('Open Shell', 'auto_config_open_shell'),
|
||||
MenuEntry('Apply ITS Settings', 'auto_apply_its_settings', selected=False),
|
||||
MenuEntry('Open-Shell', 'auto_config_open_shell', selected=False),
|
||||
MenuEntry('Disable Chrome Notifications', 'auto_disable_chrome_notifications', selected=False),
|
||||
MenuEntry('Disable Fast Startup', 'auto_disable_fast_startup', selected=False),
|
||||
MenuEntry('Disable Password Expiration', 'auto_disable_password_expiration'),
|
||||
MenuEntry('Disable Telemetry', 'auto_shutup_10'),
|
||||
MenuEntry('Enable BSoD MiniDumps', 'auto_enable_bsod_minidumps'),
|
||||
MenuEntry('Enable Hibernation', 'auto_enable_hibernation', selected=False),
|
||||
MenuEntry('Enable RegBack', 'auto_enable_regback'),
|
||||
MenuEntry('Enable System Restore', 'auto_system_restore_enable'),
|
||||
MenuEntry('Set System Restore Size', 'auto_system_restore_set_size'),
|
||||
|
|
@ -56,7 +63,7 @@ BASE_MENUS = {
|
|||
MenuEntry('Create System Restore', 'auto_system_restore_create'),
|
||||
),
|
||||
'System Information': (
|
||||
MenuEntry('AIDA64 Report', 'auto_export_aida64_report'),
|
||||
MenuEntry('AIDA64 Report', 'auto_export_aida64_report', selected=False),
|
||||
MenuEntry('Backup Registry', 'auto_backup_registry'),
|
||||
),
|
||||
'System Summary': (
|
||||
|
|
@ -70,9 +77,9 @@ BASE_MENUS = {
|
|||
),
|
||||
'Run Programs': (
|
||||
MenuEntry('Device Manager', 'auto_open_device_manager'),
|
||||
MenuEntry('HWiNFO Sensors', 'auto_open_hwinfo_sensors'),
|
||||
MenuEntry('Microsoft Store Updates', 'auto_open_microsoft_store_updates'),
|
||||
MenuEntry('Snappy Driver Installer', 'auto_open_snappy_driver_installer_origin'),
|
||||
MenuEntry('Webcam Tests', 'auto_open_mic_and_webcam_tests'),
|
||||
MenuEntry('Windows Activation', 'auto_open_windows_activation'),
|
||||
MenuEntry('Windows Updates', 'auto_open_windows_updates'),
|
||||
MenuEntry('XMPlay', 'auto_open_xmplay'),
|
||||
|
|
@ -89,7 +96,7 @@ PRESETS = {
|
|||
'Additional User': {
|
||||
'Configure System': (
|
||||
'Configure Browsers',
|
||||
'Open Shell',
|
||||
'Open-Shell',
|
||||
'uBlock Origin',
|
||||
'Enable BSoD MiniDumps',
|
||||
'Enable RegBack',
|
||||
|
|
@ -136,7 +143,6 @@ PRESETS = {
|
|||
),
|
||||
'Run Programs': (
|
||||
'Device Manager',
|
||||
'HWiNFO Sensors',
|
||||
'XMPlay',
|
||||
),
|
||||
},
|
||||
|
|
|
|||
79
scripts/export_bitlocker.ps1
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Export Bitlocker info
|
||||
|
||||
# Init
|
||||
$REPORT = ""
|
||||
$SKIPPED_PROPERTIES = @(
|
||||
"CapacityGB",
|
||||
"MountPoint",
|
||||
"KeyProtector"
|
||||
)
|
||||
|
||||
# Functions
|
||||
function Convert-BytesToString ($bytes) {
|
||||
If ($bytes -gt 1PB) {
|
||||
return ("{0:0.#} PB" -f ($bytes / 1PB) )
|
||||
} ElseIf ($bytes -gt 1TB) {
|
||||
return ("{0:0.#} TB" -f ($bytes / 1TB) )
|
||||
} ElseIf ($bytes -gt 1GB) {
|
||||
return ("{0:0.#} GB" -f ($bytes / 1GB) )
|
||||
} ElseIf ($bytes -gt 1MB) {
|
||||
return ("{0:0.#} MB" -f ($bytes / 1MB) )
|
||||
} ElseIf ($bytes -gt 1KB) {
|
||||
return ("{0:0.#} KB" -f ($bytes / 1KB) )
|
||||
} Else {
|
||||
return ("{0} B" -f $bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Build report
|
||||
$system_drive = $env:SystemDrive
|
||||
if ($system_drive -eq "X:") {
|
||||
# Assuming we're running in WinPE
|
||||
$system_drive = "C:"
|
||||
}
|
||||
Get-BitlockerVolume -MountPoint $system_drive | ForEach-Object {
|
||||
$bitlocker_volume = $_
|
||||
$REPORT += ("`n`nDrive {0}`n---`n" -f $bitlocker_volume.MountPoint)
|
||||
|
||||
# Size info
|
||||
$volume = Get-Volume -DriveLetter $bitlocker_volume.MountPoint[0]
|
||||
$total = Convert-BytesToString ($volume.Size)
|
||||
$used = Convert-BytesToString ($volume.Size - $volume.SizeRemaining)
|
||||
if ($volume.Size -gt 0) {
|
||||
$REPORT += ("Size: {0} ({1} used)`n" -f $total, $used)
|
||||
} else {
|
||||
$REPORT += "Size: Unknown`n"
|
||||
}
|
||||
|
||||
# Volume info
|
||||
$bitlocker_volume |
|
||||
Get-Member -MemberType Property |
|
||||
Where-Object {! $SKIPPED_PROPERTIES.Contains($_.Name)} |
|
||||
ForEach-Object {
|
||||
$name = $_.Name
|
||||
if ($bitlocker_volume.$name -ne $null) {
|
||||
$REPORT += ("{0}: {1}`n" -f $name, $bitlocker_volume.$name)
|
||||
}
|
||||
}
|
||||
|
||||
# Key info
|
||||
$bitlocker_volume.KeyProtector |
|
||||
ForEach-Object {
|
||||
$key = $_
|
||||
$REPORT += "Key Slot:`n"
|
||||
$key |
|
||||
Get-Member -MemberType Property |
|
||||
ForEach-Object {
|
||||
$name = $_.Name
|
||||
if ($key.$name -ne $null -and $key.$name -ne "") {
|
||||
$REPORT += ("... {0}: {1}`n" -f $name, $key.$name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Show report
|
||||
Write-Host $REPORT.Trim()
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
4
scripts/hw-info-helper
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/zsh
|
||||
#
|
||||
|
||||
sudo hw-info | less -S --use-color
|
||||
|
|
@ -56,6 +56,7 @@ for %%f in (%*) do (
|
|||
if defined _backups mkdir "%client_dir%\Backups">nul 2>&1
|
||||
if defined _logs (
|
||||
mkdir "%log_dir%\%KIT_NAME_FULL%">nul 2>&1
|
||||
mkdir "%log_dir%\d7II">nul 2>&1
|
||||
mkdir "%log_dir%\Tools">nul 2>&1)
|
||||
if defined _office mkdir "%client_dir%\Office">nul 2>&1
|
||||
if defined _quarantine mkdir "%client_dir%\Quarantine">nul 2>&1
|
||||
|
|
|
|||
|
|
@ -1,78 +1,15 @@
|
|||
"""WizardKit: Launch Snappy Driver Installer Origin"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
from subprocess import CompletedProcess
|
||||
|
||||
import wk
|
||||
from wk.cfg.net import SDIO_SERVER
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
MOUNT_EXCEPTIONS = (
|
||||
RuntimeError,
|
||||
wk.exe.subprocess.CalledProcessError,
|
||||
)
|
||||
SDIO_LOCAL_PATH = wk.kit.tools.get_tool_path("SDIO", "SDIO")
|
||||
SDIO_REMOTE_PATH = wk.io.get_path_obj(
|
||||
(
|
||||
fr'\\{SDIO_SERVER["Address"]}\{SDIO_SERVER["Share"]}\{SDIO_SERVER["Path"]}'
|
||||
fr'\SDIO{"64" if wk.os.win.ARCH == "64" else ""}.exe'
|
||||
),
|
||||
resolve=False,
|
||||
)
|
||||
|
||||
# Functions
|
||||
def try_again() -> bool:
|
||||
"""Ask to try again or quit."""
|
||||
if wk.ui.cli.ask(' Try again?'):
|
||||
return True
|
||||
if not wk.ui.cli.ask(' Use local version?'):
|
||||
wk.ui.cli.abort()
|
||||
return False
|
||||
|
||||
|
||||
def use_network_sdio() -> bool:
|
||||
"""Try to mount SDIO server."""
|
||||
use_network = False
|
||||
def _mount_server() -> CompletedProcess:
|
||||
print('Connecting to server... (Press CTRL+c to use local copy)')
|
||||
return wk.net.mount_network_share(SDIO_SERVER, read_write=False)
|
||||
|
||||
# Bail early
|
||||
if not SDIO_SERVER['Address']:
|
||||
return use_network
|
||||
|
||||
# Main loop
|
||||
while True:
|
||||
try:
|
||||
proc = _mount_server()
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except MOUNT_EXCEPTIONS as err:
|
||||
wk.ui.cli.print_error(f' {err}')
|
||||
if not try_again():
|
||||
break
|
||||
else:
|
||||
if proc.returncode == 0:
|
||||
# Network copy available
|
||||
use_network = True
|
||||
break
|
||||
|
||||
# Failed to mount
|
||||
wk.ui.cli.print_error(' Failed to mount server')
|
||||
if not try_again():
|
||||
break
|
||||
|
||||
# Done
|
||||
return use_network
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
wk.ui.cli.set_title(
|
||||
f'{wk.cfg.main.KIT_NAME_FULL}: Snappy Driver Installer Origin Launcher',
|
||||
)
|
||||
log_dir = wk.log.format_log_path(tool=True).parent
|
||||
USE_NETWORK = False
|
||||
|
||||
# Windows 11 workaround
|
||||
if wk.os.win.OS_VERSION == 11:
|
||||
|
|
@ -82,18 +19,6 @@ if __name__ == '__main__':
|
|||
if any([wk.os.win.get_service_status(s) != 'stopped' for s in appid_services]):
|
||||
raise wk.std.GenericWarning('Failed to stop AppID services')
|
||||
|
||||
# Try to mount server
|
||||
try:
|
||||
USE_NETWORK = use_network_sdio()
|
||||
except KeyboardInterrupt:
|
||||
wk.ui.cli.abort()
|
||||
|
||||
# Run SDIO
|
||||
EXE_PATH = SDIO_LOCAL_PATH
|
||||
if USE_NETWORK:
|
||||
EXE_PATH = SDIO_REMOTE_PATH
|
||||
print('Using network copy!')
|
||||
else:
|
||||
print('Using local copy!')
|
||||
cmd = [EXE_PATH, '-log_dir', log_dir]
|
||||
wk.exe.run_program(cmd, check=False, cwd=EXE_PATH.parent)
|
||||
sdio_path = wk.kit.tools.get_sdio_path(interactive=True)
|
||||
cmd = [sdio_path, '-log_dir', log_dir]
|
||||
wk.exe.popen_program(cmd, cwd=sdio_path.parent)
|
||||
|
|
|
|||
9
scripts/launch_stress_tests.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
"""WizardKit: Launch Stress Tests Tool"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import wk
|
||||
|
||||
wk.kit.tools.run_tool('CoreTemp', 'CoreTemp', popen=True)
|
||||
wk.kit.tools.run_tool('FurMark2', 'FurMark_GUI', popen=True)
|
||||
wk.kit.tools.run_tool('Prime95', 'prime95', popen=True)
|
||||
wk.kit.tools.run_tool('RUST', 'RUST', popen=True)
|
||||
9
scripts/launch_stress_tests_classic.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
"""WizardKit: Launch Stress Tests Tool"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import wk
|
||||
|
||||
wk.kit.tools.run_tool('CoreTemp', 'CoreTemp', popen=True)
|
||||
wk.kit.tools.run_tool('FurMark', 'FurMark', popen=True)
|
||||
wk.kit.tools.run_tool('Prime95', 'prime95', popen=True)
|
||||
wk.kit.tools.run_tool('RUST', 'RUST', popen=True)
|
||||
|
|
@ -11,9 +11,13 @@
|
|||
"wk/ui/__init__.py" = ["F401"]
|
||||
|
||||
# Long lines
|
||||
"wk/borrowed/acpi.py" = ["E501", "F841"]
|
||||
"auto_setup.py" = ["E501"]
|
||||
"wk/cfg/ddrescue.py" = ["E501"]
|
||||
"wk/cfg/hw.py" = ["E501"]
|
||||
"wk/cfg/launchers.py" = ["E501"]
|
||||
"wk/cfg/setup.py" = ["E501"]
|
||||
"wk/cfg/sources.py" = ["E501"]
|
||||
|
||||
# Misc
|
||||
"set_lp8550_slope.py" = ["E501", "E741"]
|
||||
"wk/borrowed/acpi.py" = ["E501", "F841"]
|
||||
|
|
|
|||
103
scripts/set_lp8550_slope.py
Executable file
|
|
@ -0,0 +1,103 @@
|
|||
#!/usr/bin/env python3
|
||||
###
|
||||
# Dependencies: Linux, python3, python3-smbus
|
||||
# Usage: `sudo ./set_lp8550_slope.py`
|
||||
#
|
||||
# Default slope is 0b101=200ms, can be adjusted below
|
||||
# Tested with Ubuntu 20.04 on A1466 2013
|
||||
# All original EEPROM registers value:
|
||||
# - A0: 0b01111111
|
||||
# - A1: 0b10110101
|
||||
# - A2: 0b10111111
|
||||
# - A3: 0b01111011
|
||||
# - A4: 0b00101000
|
||||
# - A5: 0b11001111
|
||||
# - A6: 0b01100100
|
||||
# - A7: 0b00101101
|
||||
###
|
||||
|
||||
import time
|
||||
|
||||
import smbus2
|
||||
|
||||
config = {
|
||||
'smbus_address': 0, # SMBus address, can use `i2cdetect -l` to find it
|
||||
'device_address': 0x2C, # LP8550 7 bits address
|
||||
'slope': (1, 0, 1) # The new slope to set, MSB first, see lp8550 datasheet 8.6.2.3 SLOPE
|
||||
}
|
||||
|
||||
lp8550_regs = {
|
||||
'EEPROMControl': 0x72,
|
||||
'EEPROMAddress1': 0xA1 # default value for this register is 0b10110101 on original 820-3437
|
||||
}
|
||||
|
||||
def bitstolist(val, regsize=8):
|
||||
return [(val >> i) & 0b1 for i in range(0, regsize)]
|
||||
|
||||
def listtobits(l, regsize=8):
|
||||
val = 0
|
||||
for i in range(0, regsize):
|
||||
val += l[i] << i
|
||||
return val
|
||||
|
||||
def read_reg(bus, dev_address, reg_name):
|
||||
return bitstolist(bus.read_byte_data(dev_address, lp8550_regs[reg_name]))
|
||||
|
||||
def write_reg(bus, dev_address, reg_name, reg):
|
||||
print(f'Writing { listtobits(reg)} to {dev_address}, {lp8550_regs[reg_name]}')
|
||||
bus.write_byte_data(dev_address, lp8550_regs[reg_name], listtobits(reg))
|
||||
|
||||
def get_slope(bus, dev_address):
|
||||
return read_reg(bus, dev_address, 'EEPROMAddress1')[0:3][::-1]
|
||||
|
||||
def set_slope(bus, dev_address, slope):
|
||||
print('Original slope: ', get_slope(bus, dev_address))
|
||||
|
||||
EEPROMAddress1 = read_reg(bus, dev_address, 'EEPROMAddress1')
|
||||
EEPROMAddress1[0:3] = slope[::-1]
|
||||
|
||||
write_reg(bus, dev_address, 'EEPROMAddress1', EEPROMAddress1)
|
||||
|
||||
print('New slope: ', get_slope(bus, dev_address))
|
||||
|
||||
def write_nvm(bus, dev_address):
|
||||
print('Writing to non-volatile memory')
|
||||
EEPROMControl = [0]*8
|
||||
EEPROMControl[2] = 1 # EE_INIT=1
|
||||
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
|
||||
|
||||
EEPROMControl[1] = 1 # EE_PROG=1
|
||||
EEPROMControl[2] = 0 # EE_INIT=0
|
||||
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
|
||||
|
||||
time.sleep(0.2) # wait 200ms
|
||||
|
||||
EEPROMControl[1] = 0 # EE_PROG=0
|
||||
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
|
||||
|
||||
def load_nvm(bus, dev_address):
|
||||
print('Loading from non-volatile memory')
|
||||
EEPROMControl = [0]*8
|
||||
EEPROMControl[2] = 1 # EE_INIT=1
|
||||
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
|
||||
|
||||
EEPROMControl[0] = 1 # EE_READ=1
|
||||
EEPROMControl[2] = 0 # EE_INIT=0
|
||||
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
|
||||
|
||||
time.sleep(0.2) # wait 200ms
|
||||
|
||||
EEPROMControl[0] = 0 # EE_READ=0
|
||||
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
|
||||
|
||||
def main():
|
||||
bus = smbus2.SMBus(config['smbus_address'])
|
||||
|
||||
set_slope(bus, config['device_address'], config['slope'])
|
||||
write_nvm(bus, config['device_address'])
|
||||
load_nvm(bus, config['device_address'])
|
||||
|
||||
print('Slope in non-volatile memory: ', get_slope(bus,config['device_address']))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -25,6 +25,53 @@ if PLATFORM not in ('macOS', 'Linux'):
|
|||
|
||||
|
||||
# Functions
|
||||
def compress(reason):
|
||||
dest = pathlib.Path(f'~/{reason}_{NOW.strftime("%Y-%m-%dT%H%M%S%z")}.txz')
|
||||
dest = dest.expanduser().resolve()
|
||||
cmd = ['tar', 'caf', dest.name, LOG_DIR.name]
|
||||
wk.exe.run_program(cmd, check=False)
|
||||
return dest
|
||||
|
||||
|
||||
def compress_live_macos(reason):
|
||||
"""Compress log_dir using the RAM disk."""
|
||||
dest = pathlib.Path(
|
||||
f'/Volumes/RAM_Disk/{reason}_{NOW.strftime("%Y-%m-%dT%H%M%S%z")}.txz',
|
||||
)
|
||||
tmp_file = dest.with_suffix('.tar')
|
||||
cmd = ['tar', '-cf', tmp_file.name, LOG_DIR.name]
|
||||
wk.exe.run_program(cmd, check=False)
|
||||
cmd = ['xz', '-z', tmp_file.name]
|
||||
wk.exe.run_program(cmd, check=False)
|
||||
tmp_file = tmp_file.with_suffix('.tar.xz')
|
||||
tmp_file.rename(dest.name)
|
||||
return dest
|
||||
|
||||
|
||||
def compress_and_upload(reason='Testing'):
|
||||
"""Upload compressed log_dir to the crash server."""
|
||||
server = wk.cfg.net.CRASH_SERVER
|
||||
upload_args = {
|
||||
'headers': server['Headers'],
|
||||
'auth': (server['User'], server['Pass']),
|
||||
}
|
||||
|
||||
# Compress LOG_DIR (relative to parent dir)
|
||||
if os.path.exists('/.wk-live-macos'):
|
||||
dest = compress_live_macos(reason)
|
||||
else:
|
||||
dest = compress(reason)
|
||||
upload_args['data'] = dest.read_bytes()
|
||||
|
||||
# Upload compressed data
|
||||
url = f'{server["Url"]}/{dest.name}'
|
||||
result = requests.put(url, **upload_args)
|
||||
|
||||
# Check result
|
||||
if not result.ok:
|
||||
raise wk.std.GenericError('Failed to upload logs')
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Upload logs for review."""
|
||||
lines = []
|
||||
|
|
@ -42,7 +89,7 @@ def main() -> None:
|
|||
|
||||
# Get reason note
|
||||
while True:
|
||||
text = wk.ui.cli.input_text('> ')
|
||||
text = wk.ui.cli.input_text('> ', allow_empty=True)
|
||||
if not text:
|
||||
lines.append('')
|
||||
break
|
||||
|
|
@ -51,9 +98,11 @@ def main() -> None:
|
|||
_f.write('\n'.join(lines))
|
||||
|
||||
# Compress and upload logs
|
||||
os.chdir(LOG_DIR.parent)
|
||||
print(os.getcwd())
|
||||
result = try_and_print.run(
|
||||
message='Uploading logs...',
|
||||
function=upload_log_dir,
|
||||
function=compress_and_upload,
|
||||
reason='Review',
|
||||
)
|
||||
if not result['Failed']:
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
"""WizardKit: wk module init"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import platform
|
||||
from sys import stderr, version_info
|
||||
|
||||
from . import cfg
|
||||
from . import clone
|
||||
from . import debug
|
||||
from . import exe
|
||||
from . import graph
|
||||
from . import hw
|
||||
from . import io
|
||||
from . import kit
|
||||
|
|
@ -19,6 +19,10 @@ from . import setup
|
|||
from . import std
|
||||
from . import ui
|
||||
|
||||
if platform.system() != 'Windows':
|
||||
from wk import graph
|
||||
from wk import osticket
|
||||
|
||||
|
||||
# Check env
|
||||
if version_info < (3, 10):
|
||||
|
|
|
|||
36
scripts/wk/cfg/1201_Root_CA.crt
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIGTzCCBDegAwIBAgIBfDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMCVVMx
|
||||
DzANBgNVBAgTBk9yZWdvbjERMA8GA1UEBxMIUG9ydGxhbmQxHTAbBgNVBAoTFDEy
|
||||
MDEgQ29tcHV0ZXIgUmVwYWlyMSMwIQYDVQQLExoxMjAxIENlcnRpZmljYXRlIEF1
|
||||
dGhvcml0eTEVMBMGA1UEAxMMMTIwMSBSb290IENBMSIwIAYJKoZIhvcNAQkBFhNt
|
||||
YW5hZ2VtZW50QDEyMDEuY29tMB4XDTE4MDgyMDA2MDEwMFoXDTM4MDgyMDA2MDEw
|
||||
MFowgbAxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZPcmVnb24xETAPBgNVBAcTCFBv
|
||||
cnRsYW5kMR0wGwYDVQQKExQxMjAxIENvbXB1dGVyIFJlcGFpcjEjMCEGA1UECxMa
|
||||
MTIwMSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFTATBgNVBAMTDDEyMDEgUm9vdCBD
|
||||
QTEiMCAGCSqGSIb3DQEJARYTbWFuYWdlbWVudEAxMjAxLmNvbTCCAiIwDQYJKoZI
|
||||
hvcNAQEBBQADggIPADCCAgoCggIBANGYohJk5/CC/p14R7EpnhdEUF7Wvlnt8yuF
|
||||
dtuyStlIGkLxPMlj9hQfoLDplQqlKBefTaI3WwrI/Hndso+jStLKgtRWRdyNB34K
|
||||
AWqT04zXYGicdi3fqaMhEC4SPyX1tRXU2e9kjtIJ21AZx2F40NUjfOMKLVymZgXm
|
||||
gkG1oA/BSzE8vIidrd/lJPwo0u+EYFa87y+9SHS93Ze1AVoTVqUzSMkjqt+6YIzJ
|
||||
4XBD7UBvps0Mnd18HMUlXHFXusUL1K921W3wDVcMlNIIA8MJjQk+aVS/1EGSn+81
|
||||
C+r40x64lYkyh0ZUAHkVXUC/BUfa0SKx1Nfa4mSvtyPnUCb7Dir8MkTDKgopGCok
|
||||
KmW+VvE2H8AEPCbcctFmhdip19laYxzyDhZ5wiQN6AOg64cWvDf6/uT9hyPvxkj1
|
||||
ps5vWElryzawTE7h1BI8liMqwsG1Y7cc6D0PABxPsp4iR8pde0oZtpLnEvejRodo
|
||||
zz3BGvZjq+pHtRMjL+yiDtdAL+K+7/e7gNCQBIGsphahWIOf7TczWVgMNclTNxl3
|
||||
WZjKkOEs7j+prRTDvffV6H32+Tk5TwgMsfvnY4a37CkJ0L0d1JhWj9wO+gESfg3W
|
||||
8yvt3hfcj3NOUMJWhJstqlIeX8dj7vVcMhjNvYJxabJmJgk+DNlHe55fXDGJ1CLO
|
||||
E0EbRTyBAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFM+hXjFx
|
||||
6BldZFBQW1Pn/Yp3vbw+MAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAAcw
|
||||
HgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOC
|
||||
AgEALWcnu3auMSnSSF/kOiLvJ4RAnHZebGYNcUWM14u1K1/XtTB7AFzQIHX7BcDH
|
||||
m/z4UEyhl9EdR5Bgf2Szuk+8+LyGqcdAdbPoK+bmcwwL8lufDnlIYBThKIBfU2Xw
|
||||
vw41972B+HH5r1TZXve1EdJaLyImbxmq5s41oH7djGC+sowtyGuVqP7RBguXBGiJ
|
||||
At1yfdPWVaxLmE8QFknkIvpgTmELpxasTfvgnQBenA3Ts0Z2hwN4796hLbRzGsb8
|
||||
4hKWAfQDP0klzXKRRyVeAueXxj/FcNZilYxv15MqMc4qrUiW0hXHluQM1yceNjNZ
|
||||
SE4Igi1Ap71L4PpgkHIDfZD908UexGGkql+p4EWrpnXUYWTa0sHg1bFKQntgpyFg
|
||||
86Ug0Q7ZNhImENzeigZknL0ceIdaNUCs7UPrkqpUSJR2yujp1JC3tX1LgKZw8B3J
|
||||
fQx/8h3zzNuz5dVtr1wUJaUD0nGhMIRBEXb2t4jupEISSTN1pkHPcbNzhAQXjVUA
|
||||
CJxnnz3jmyGsNCoQf7NWfaN6RSRTWehsC6m7JvPvoU2EZoQkSlNOv4xZuFpEx0u7
|
||||
MFDtC1cSGPH7YwYXPVc45xAMC6Ni8mvq93oT89XZNHIqE8/T8aPHLwYFgu1b1r/A
|
||||
L8oMEnG5s8tG3n0DcFoOYsaIzVeP0r7B7e3zKui6DQLuu9E=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -5,7 +5,6 @@ from . import hw
|
|||
from . import launchers
|
||||
from . import log
|
||||
from . import main
|
||||
from . import music
|
||||
from . import net
|
||||
from . import repairs
|
||||
from . import setup
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
TMUX_SIDE_WIDTH = 21
|
||||
TMUX_LAYOUT = {
|
||||
'Source': {'height': 2, 'Check': True},
|
||||
'Ticket': {'height': 2, 'Check': True},
|
||||
'Started': {'width': TMUX_SIDE_WIDTH, 'Check': True},
|
||||
'Progress': {'width': TMUX_SIDE_WIDTH, 'Check': True},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,14 @@ ATTRIBUTE_COLORS = (
|
|||
# NOTE: Force 4K read block size for disks >= 3TB
|
||||
BADBLOCKS_EXTRA_LARGE_DISK = 15 * 1024**4
|
||||
BADBLOCKS_LARGE_DISK = 3 * 1024**4
|
||||
BADBLOCKS_MAX_ERRORS = 1
|
||||
BADBLOCKS_REGEX = re.compile(
|
||||
r'^Pass completed, (\d+) bad blocks found. .(\d+)/(\d+)/(\d+) errors',
|
||||
re.IGNORECASE,
|
||||
)
|
||||
BADBLOCKS_RESULTS_REGEX = re.compile(r'^(.*?)\x08.*\x08(.*)')
|
||||
BADBLOCKS_RESULTS_REGEX = re.compile(
|
||||
r'^(Checking for bad blocks .read-only test.: ).*\x08+(done|\s+).*?(\x08+)?'
|
||||
)
|
||||
BADBLOCKS_SKIP_REGEX = re.compile(r'^(Checking|\[)', re.IGNORECASE)
|
||||
CPU_TEMPS = {
|
||||
'Cooling Delta': 25,
|
||||
|
|
@ -36,6 +39,7 @@ IO_MINIMUM_TEST_SIZE = 10 * 1024**3
|
|||
IO_RATE_REGEX = re.compile(
|
||||
r'(?P<bytes>\d+) bytes.* (?P<seconds>\S+) s(?:,|ecs )',
|
||||
)
|
||||
IO_SMALL_DISK = 450 * 1000**3
|
||||
KEY_NVME = 'nvme_smart_health_information_log'
|
||||
KEY_SMART = 'ata_smart_attributes'
|
||||
KNOWN_DISK_ATTRIBUTES = {
|
||||
|
|
@ -82,9 +86,18 @@ NVME_WARNING_KEYS = (
|
|||
'reliability_degraded',
|
||||
'volatile_memory_backup_failed',
|
||||
)
|
||||
REGEX_BLOCK_GRAPH = re.compile(r'(▁|▂|▃|▄|▅|▆|▇|█)')
|
||||
REGEX_POWER_ON_TIME = re.compile(
|
||||
r'^(\d+)([Hh].*|\s+\(\d+\s+\d+\s+\d+\).*)'
|
||||
)
|
||||
REGEX_SMART_ATTRIBUTES = re.compile(
|
||||
r'^\s*(?P<decimal>\d+) / (?P<hex>\w\w): (?P<data>.*)$',
|
||||
)
|
||||
REGEX_VOLUME = re.compile(
|
||||
r'^(?P<dev>.*?) '
|
||||
r'(?P<result>(APFS|CoreStorage) container|Failed to mount|Mounted on|\S+$)'
|
||||
r'($| (?P<path>.*) \((?P<details>.*)\))'
|
||||
)
|
||||
SMART_SELF_TEST_START_TIMEOUT_IN_SECONDS = 120
|
||||
SMC_IDS = {
|
||||
# Sources: https://github.com/beltex/SMCKit/blob/master/SMCKit/SMC.swift
|
||||
|
|
@ -134,15 +147,16 @@ SMC_IDS = {
|
|||
'TS0C': {'CPU Temp': False, 'Source': 'CPU B DIMM Exit Ambient'},
|
||||
}
|
||||
STATUS_COLORS = {
|
||||
'Passed': 'GREEN',
|
||||
'Aborted': 'YELLOW',
|
||||
'N/A': 'YELLOW',
|
||||
'Skipped': 'YELLOW',
|
||||
'Unknown': 'YELLOW',
|
||||
'Working': 'YELLOW',
|
||||
'Denied': 'RED',
|
||||
'ERROR': 'RED',
|
||||
'Failed': 'RED',
|
||||
'Done': 'CYAN',
|
||||
'Passed': 'GREEN',
|
||||
'Aborted': 'YELLOW',
|
||||
'N/A': 'YELLOW',
|
||||
'Skipped': 'YELLOW',
|
||||
'Unknown': 'YELLOW',
|
||||
'Working': 'YELLOW',
|
||||
'Denied': 'RED',
|
||||
'ERROR': 'RED',
|
||||
'Failed': 'RED',
|
||||
'TimedOut': 'RED',
|
||||
}
|
||||
TEMP_COLORS = {
|
||||
|
|
@ -165,8 +179,9 @@ THRESH_SSD_MIN = 90 * 1024**2
|
|||
THRESH_SSD_AVG_HIGH = 135 * 1024**2
|
||||
THRESH_SSD_AVG_LOW = 100 * 1024**2
|
||||
# VOLUME THRESHOLDS in percent
|
||||
VOLUME_WARNING_THRESHOLD = 70
|
||||
VOLUME_FAILURE_THRESHOLD = 85
|
||||
VOLUME_WARNING_THRESHOLD = 70
|
||||
VOLUME_FAILURE_THRESHOLD = 85
|
||||
VOLUME_SIZE_THRESHOLD = 30 # In GB
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -133,6 +133,15 @@ LAUNCHERS = {
|
|||
'L_ARGS': '-nodb',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
"Fab's Autobackup Pro": {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'AutoBackupPro',
|
||||
'L_ITEM': 'autobackup6pro.exe',
|
||||
'Extra Code': [
|
||||
r'call "%bin%\Scripts\init_client_dir.cmd"',
|
||||
r'reg add HKCU\Software\1201-WizardKit /v FabLastRun /t REG_SZ /d %iso_date% /f >nul 2>&1',
|
||||
],
|
||||
},
|
||||
'FastCopy (as ADMIN)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'FastCopy',
|
||||
|
|
@ -233,40 +242,76 @@ LAUNCHERS = {
|
|||
'L_PATH': 'FurMark',
|
||||
'L_ITEM': 'FurMark.exe',
|
||||
},
|
||||
'HWiNFO': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'HWiNFO',
|
||||
'L_ITEM': 'HWiNFO.exe',
|
||||
'Extra Code': [
|
||||
r'for %%a in (32 64) do (',
|
||||
r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r')',
|
||||
],
|
||||
},
|
||||
'HWiNFO (Sensors)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'HWiNFO',
|
||||
'L_ITEM': 'HWiNFO.exe',
|
||||
'Extra Code': [
|
||||
r'for %%a in (32 64) do (',
|
||||
r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r' (echo SensorsOnly=1)>>"%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r')',
|
||||
],
|
||||
},
|
||||
'Notepad++': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'notepadplusplus',
|
||||
'L_ITEM': 'notepadplusplus.exe',
|
||||
},
|
||||
'Prime95': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'Prime95',
|
||||
'L_ITEM': 'prime95.exe',
|
||||
},
|
||||
'PuTTY': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'PuTTY',
|
||||
'L_ITEM': 'PUTTY.EXE',
|
||||
},
|
||||
'ShutUp10': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'ShutUp10',
|
||||
'L_ITEM': 'OOSU10.exe',
|
||||
},
|
||||
'ShutUp10 (1201 Minimal Selection)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'ShutUp10',
|
||||
'L_ITEM': 'OOSU10.exe',
|
||||
'L_ARGS': '1201.cfg',
|
||||
},
|
||||
'Stress Tests (Classic)': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
'L_ITEM': 'launch_stress_tests_classic.py',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'Stress Tests': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
'L_ITEM': 'launch_stress_tests.py',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'Windows Repair AIO': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'WinRepairAIO',
|
||||
'L_ITEM': 'Repair_Windows.exe',
|
||||
'L_ELEV': 'True',
|
||||
'Extra Code': [
|
||||
r'copy /y "%bin%\WinRepairAIO\__empty.ini" "%bin%\WinRepairAIO\settings.ini"',
|
||||
],
|
||||
},
|
||||
'Windows Repair AIO (Fix Associations)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'WinRepairAIO',
|
||||
'L_ITEM': 'Repair_Windows.exe',
|
||||
'L_ELEV': 'True',
|
||||
'Extra Code': [
|
||||
r'copy /y "%bin%\WinRepairAIO\__associations.ini" "%bin%\WinRepairAIO\settings.ini"',
|
||||
],
|
||||
},
|
||||
'Windows Repair AIO (Fix Permissions)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'WinRepairAIO',
|
||||
'L_ITEM': 'Repair_Windows.exe',
|
||||
'L_ELEV': 'True',
|
||||
'Extra Code': [
|
||||
r'copy /y "%bin%\WinRepairAIO\__permissions.ini" "%bin%\WinRepairAIO\settings.ini"',
|
||||
],
|
||||
},
|
||||
'WinSCP': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'WinSCP',
|
||||
'L_ITEM': 'WinSCP.exe',
|
||||
},
|
||||
'WizTree': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'WizTree',
|
||||
|
|
@ -277,7 +322,7 @@ LAUNCHERS = {
|
|||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'XMPlay',
|
||||
'L_ITEM': 'xmplay.exe',
|
||||
'L_ARGS': r'"%bin%\XMPlay\music.7z"',
|
||||
'L_ARGS': r'"%bin%\XMPlay\music.m3u"',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,21 +8,21 @@ NOTE: Non-standard formating is used for BASH/BATCH/PYTHON compatibility
|
|||
# Features
|
||||
ENABLED_OPEN_LOGS=False
|
||||
ENABLED_TICKET_NUMBERS=False
|
||||
ENABLED_UPLOAD_DATA=False
|
||||
ENABLED_UPLOAD_DATA=True
|
||||
|
||||
# Main Kit
|
||||
ARCHIVE_PASSWORD='Abracadabra'
|
||||
KIT_NAME_FULL='WizardKit'
|
||||
KIT_NAME_SHORT='WK'
|
||||
SUPPORT_MESSAGE='Please let 2Shirt know by opening an issue on Gitea'
|
||||
ARCHIVE_PASSWORD='Sorted1201'
|
||||
KIT_NAME_FULL='1201-WizardKit'
|
||||
KIT_NAME_SHORT='1201'
|
||||
SUPPORT_MESSAGE='Please let support know by opening an issue on Gitea'
|
||||
|
||||
# Text Formatting
|
||||
INDENT=4
|
||||
WIDTH=32
|
||||
|
||||
# Live Linux
|
||||
ROOT_PASSWORD='Abracadabra'
|
||||
TECH_PASSWORD='Abracadabra'
|
||||
ROOT_PASSWORD='1201 loves computers!'
|
||||
TECH_PASSWORD='Sorted1201'
|
||||
|
||||
# Time Zones
|
||||
## See 'timedatectl list-timezones' for valid Linux values
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
"""WizardKit: Config - Music Sources"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
MUSIC_MOD = (
|
||||
('33432', 'ambrozia.xm'),
|
||||
('33460', 'amigatre.mod'),
|
||||
('34594', 'CHARIOT.S3M'),
|
||||
('34596', 'BUTTERFL.XM'),
|
||||
('34654', 'CTGOBLIN.S3M'),
|
||||
('35151', 'bananasplit.mod'),
|
||||
('35280', 'DEADLOCK.XM'),
|
||||
('38591', 'compo_liam.xm'),
|
||||
('39987', 'crystald.s3m'),
|
||||
('40475', 'ELYSIUM.MOD'),
|
||||
('42146', 'enigma.mod'),
|
||||
('42519', 'GHOST2.MOD'),
|
||||
('42560', 'GSLINGER.MOD'),
|
||||
('42872', 'existing.xm'),
|
||||
('50427', 'nf-stven.xm'),
|
||||
('51549', 'overture.mod'),
|
||||
('54250', 'SATELL.S3M'),
|
||||
('54313', 'realmk.s3m'),
|
||||
('55789', 'scrambld.mod'),
|
||||
('57934', 'spacedeb.mod'),
|
||||
('59344', 'stardstm.mod'),
|
||||
('60395', '2ND_PM.S3M'),
|
||||
('66187', 'external.xm'),
|
||||
('66343', 'beek-substitutionology.it'),
|
||||
('67561', 'radix-unreal_superhero.xm'),
|
||||
('70829', 'inside_out.s3m'),
|
||||
('83779', 'beyond_music.mod'),
|
||||
('104208', 'banana_boat.mod'),
|
||||
('114971', 'tilbury_fair.mod'),
|
||||
('132563', 'ufo_tune.mod'),
|
||||
('135906', 'magnetik_girl.xm'),
|
||||
('140628', 'autumn_in_budapest.xm'),
|
||||
('143198', 'summer_memories_3.xm'),
|
||||
('144405', 'hillbilly_billyboy.xm'),
|
||||
('154795', '4mat_-_eternity.xm'),
|
||||
('155845', 'bookworm.mo3'),
|
||||
('155914', 'battleofsteel.xm'),
|
||||
('158975', '1_channel_moog.it'),
|
||||
('165495', 'trans.s3m'),
|
||||
('168513', 'necros_-_introspection.s3m'),
|
||||
('169628', 'radix_-_feng_shui_schematics.xm'),
|
||||
('175238', 'unknown48_-_twilight.mod'),
|
||||
)
|
||||
MUSIC_SNES = (
|
||||
'actr',
|
||||
'crock',
|
||||
'ct',
|
||||
'dkc',
|
||||
'dkq',
|
||||
'ff6',
|
||||
'fz',
|
||||
'loz3',
|
||||
'mmx',
|
||||
'ptws',
|
||||
'scv4',
|
||||
'sf',
|
||||
'sf2',
|
||||
'sgng',
|
||||
'smk',
|
||||
'smw',
|
||||
'yi',
|
||||
'zamn',
|
||||
)
|
||||
MUSIC_SNES_BAD = {
|
||||
'ct': ['ct-s*', 'ct-v*'],
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
@ -4,38 +4,41 @@
|
|||
|
||||
# Servers
|
||||
BACKUP_SERVERS = {
|
||||
#'Server One': {
|
||||
# 'Address': '10.0.0.10',
|
||||
# 'Share': 'Backups',
|
||||
# 'RO-User': 'restore',
|
||||
# 'RO-Pass': 'Abracadabra',
|
||||
# 'RW-User': 'backup',
|
||||
# 'RW-Pass': 'Abracadabra',
|
||||
# },
|
||||
#'Server Two': {
|
||||
# 'Address': 'servertwo.example.com',
|
||||
# 'Share': 'Backups',
|
||||
# 'RO-User': 'restore',
|
||||
# 'RO-Pass': 'Abracadabra',
|
||||
# 'RW-User': 'backup',
|
||||
# 'RW-Pass': 'Abracadabra',
|
||||
# },
|
||||
'Anaconda': {
|
||||
'Address': 'anaconda.1201.com',
|
||||
'Share': 'Backups',
|
||||
'RO-User': 'cx',
|
||||
'RO-Pass': 'cx',
|
||||
'RW-User': 'backup',
|
||||
'RW-Pass': '1201 loves computers!',
|
||||
},
|
||||
}
|
||||
BENCHMARK_SERVER = {
|
||||
'Name': 'Nextcloud',
|
||||
'Short Url': 'https://1201.ddns.net/nextcloud/f/14775',
|
||||
'Url': 'https://1201.ddns.net/nextcloud/public.php/webdav/Benchmarks',
|
||||
'User': 'LHdPTofNBQNGBbX',
|
||||
'Pass': '',
|
||||
}
|
||||
CRASH_SERVER = {
|
||||
#'Name': 'CrashServer',
|
||||
#'Url': '',
|
||||
#'User': '',
|
||||
#'Pass': '',
|
||||
#'Headers': {'X-Requested-With': 'XMLHttpRequest'},
|
||||
'Name': 'Nextcloud',
|
||||
'Url': 'https://1201.ddns.net/nextcloud/public.php/webdav/Issues',
|
||||
'User': 'fnyzeK64rKxcEL4',
|
||||
'Pass': '',
|
||||
'Headers': {'X-Requested-With': 'XMLHttpRequest'},
|
||||
}
|
||||
SDIO_SERVER = {
|
||||
'Address': '',
|
||||
'Share': '',
|
||||
'Path': '',
|
||||
'RO-User': '',
|
||||
'RO-Pass': '',
|
||||
'Address': 'anaconda.1201.com',
|
||||
'Share': 'Snappy',
|
||||
'Path': '\\',
|
||||
'RO-User': 'cx',
|
||||
'RO-Pass': 'cx',
|
||||
}
|
||||
|
||||
|
||||
# Misc
|
||||
IMGUR_CLIENT_ID='3d1ee1d38707b85'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
|
|||
17
scripts/wk/cfg/osticket.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
"""Wizard Kit: Config - osTicket"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
SQL = {
|
||||
'DB': 'osticket',
|
||||
'Host': 'osticket.1201.com',
|
||||
'Port': 3306,
|
||||
'User': 'wizardkit',
|
||||
'Pass': 'U9bJnF9eamVkfsVw',
|
||||
}
|
||||
STAFF = {
|
||||
'ID': '23',
|
||||
'Name': 'Wizard Kit',
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
"""WizardKit: Config - Repairs"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
from wk.cfg.main import KIT_NAME_FULL
|
||||
from wk.cfg.main import KIT_NAME_FULL, KIT_NAME_SHORT
|
||||
|
||||
AUTO_REPAIR_DELAY_IN_SECONDS = 3
|
||||
AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
|
||||
|
|
@ -59,7 +59,7 @@ BLEACH_BIT_CLEANERS = (
|
|||
'windows_explorer.run',
|
||||
'windows_explorer.thumbnails',
|
||||
)
|
||||
CUSTOM_POWER_PLAN_NAME = f'{KIT_NAME_FULL} Power Plan'
|
||||
CUSTOM_POWER_PLAN_NAME = f'{KIT_NAME_SHORT} Power Plan'
|
||||
CUSTOM_POWER_PLAN_DESC = 'Customized for the best experience.'
|
||||
POWER_PLANS = {
|
||||
'Balanced': '381b4222-f694-41f0-9685-ff5bb260df2e',
|
||||
|
|
|
|||
|
|
@ -9,10 +9,9 @@ BROWSER_PATHS = {
|
|||
'Microsoft Edge': 'Microsoft/Edge/Application/msedge.exe',
|
||||
'Opera': 'Opera/launcher.exe',
|
||||
}
|
||||
FAB_TIMEFRAME = 14 # If it's been at least this many days don't prompt for an AV scan
|
||||
DISABLED_ENTRIES_WINDOWS_11 = {
|
||||
# Group Name: Option Name
|
||||
'Install Software': 'Open Shell',
|
||||
'Configure System': 'Open Shell',
|
||||
}
|
||||
LIBREOFFICE_XCU_DATA = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
|
@ -29,6 +28,21 @@ REG_CHROME_UBLOCK_ORIGIN = {
|
|||
)
|
||||
},
|
||||
}
|
||||
REG_ESET_NOD32_SETTINGS = {
|
||||
'HKCU': {
|
||||
r'Software\ESET\ESET Security\CurrentVersion\gui\UI_CONFIG': (
|
||||
('FullScreenMode', 0, 'DWORD'),
|
||||
('ShowAlertStatus', 0x40000000, 'DWORD'),
|
||||
('ShowDesktopAlert', 0, 'DWORD'),
|
||||
('ShowSplash', 0, 'DWORD'),
|
||||
),
|
||||
},
|
||||
'HKLM': {
|
||||
r'Software\ESET\ESET Security\CurrentVersion\Config\Settings\LiveGrid': (
|
||||
('LiveGridFeedbackEnabled', 1, 'DWORD'),
|
||||
),
|
||||
},
|
||||
}
|
||||
REG_WINDOWS_EXPLORER = {
|
||||
'HKLM': {
|
||||
# Allow password sign-in for MS accounts
|
||||
|
|
@ -50,6 +64,12 @@ REG_WINDOWS_EXPLORER = {
|
|||
r'Software\Policies\Microsoft\Windows\DataCollection': (
|
||||
('AllowTelemetry', 0, 'DWORD'),
|
||||
),
|
||||
# Disable Timeline
|
||||
r'Software\Policies\Microsoft\Windows\System': (
|
||||
('EnableActivityFeed', 0, 'DWORD'),
|
||||
('PublishUserActivities', 0, 'DWORD'),
|
||||
('UploadUserActivities', 0, 'DWORD'),
|
||||
),
|
||||
# Disable floating Bing search widget
|
||||
r'Software\Policies\Microsoft\Edge': (
|
||||
('WebWidgetAllowed', 0, 'DWORD'),
|
||||
|
|
@ -85,6 +105,7 @@ REG_WINDOWS_EXPLORER = {
|
|||
),
|
||||
# Disable search highlights
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Feeds\DSB': (
|
||||
('OpenOnHover', 0, 'DWORD'),
|
||||
('ShowDynamicContent', 0, 'DWORD'),
|
||||
),
|
||||
# File Explorer
|
||||
|
|
@ -99,7 +120,7 @@ REG_WINDOWS_EXPLORER = {
|
|||
),
|
||||
# Hide Search button / box
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Search': (
|
||||
('SearchboxTaskbarMode', 1, 'DWORD'),
|
||||
('SearchboxTaskbarMode', 2, 'DWORD'),
|
||||
),
|
||||
# Disable search highlights from opening on hover
|
||||
r'Software\Microsoft\Windows\CurrentVersion\SearchSettings': (
|
||||
|
|
|
|||
|
|
@ -9,46 +9,45 @@ DOWNLOAD_FREQUENCY = 7
|
|||
# Sources
|
||||
SOURCES = {
|
||||
# Main
|
||||
'AVRemover32': 'https://download.eset.com/com/eset/tools/installers/av_remover/latest/avremover_nt32_enu.exe',
|
||||
'AVRemover64': 'https://download.eset.com/com/eset/tools/installers/av_remover/latest/avremover_nt64_enu.exe',
|
||||
'AdwCleaner': 'https://downloads.malwarebytes.com/file/adwcleaner',
|
||||
'Autologon32': 'http://live.sysinternals.com/Autologon.exe',
|
||||
'AdwCleaner': 'https://adwcleaner.malwarebytes.com/adwcleaner?channel=release',
|
||||
'Autologon64': 'http://live.sysinternals.com/Autologon64.exe',
|
||||
'Firefox32': 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win&lang=en-US',
|
||||
'CoreTemp64': 'https://www.alcpu.com/CoreTemp/CoreTemp64.zip',
|
||||
'ESET_NOD32_AV64': 'https://download.eset.com/com/eset/apps/home/eav/windows/latest/eav_nt64.exe',
|
||||
'EmsisoftCmd64': 'https://dl.emsisoft.com/EmsisoftCommandlineScanner64.exe',
|
||||
'Firefox64': 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=en-US',
|
||||
'HitmanPro32': 'https://dl.surfright.nl/HitmanPro.exe',
|
||||
'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe',
|
||||
'KVRT': 'https://devbuilds.s.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe',
|
||||
'MBAM': 'https://downloads.malwarebytes.com/file/mb-windows',
|
||||
'RKill': 'https://download.bleepingcomputer.com/grinler/rkill.exe',
|
||||
'RegDelNull': 'https://live.sysinternals.com/RegDelNull.exe',
|
||||
'RegDelNull64': 'https://live.sysinternals.com/RegDelNull64.exe',
|
||||
|
||||
# Build Kit
|
||||
'AIDA64': 'https://download.aida64.com/aida64engineer692.zip',
|
||||
'Adobe Reader DC': 'https://ardownload2.adobe.com/pub/adobe/reader/win/AcrobatDC/2300620360/AcroRdrDC2300620360_en_US.exe',
|
||||
'AIDA64': 'https://download.aida64.com/aida64engineer720.zip',
|
||||
'Adobe Reader DC': 'https://ardownload2.adobe.com/pub/adobe/acrobat/win/AcrobatDC/2400120615/AcroRdrDCx642400120615_en_US.exe',
|
||||
'Aria2': 'https://github.com/aria2/aria2/releases/download/release-1.36.0/aria2-1.36.0-win-32bit-build1.zip',
|
||||
'Autoruns32': 'http://live.sysinternals.com/Autoruns.exe',
|
||||
'Autoruns64': 'http://live.sysinternals.com/Autoruns64.exe',
|
||||
'BleachBit': 'https://download.bleachbit.org/BleachBit-4.4.2-portable.zip',
|
||||
'BlueScreenView32': 'http://www.nirsoft.net/utils/bluescreenview.zip',
|
||||
'BlueScreenView64': 'http://www.nirsoft.net/utils/bluescreenview-x64.zip',
|
||||
'BCUninstaller': 'https://github.com/Klocman/Bulk-Crap-Uninstaller/releases/download/v5.7/BCUninstaller_5.7_portable.zip',
|
||||
'BlueScreenView64': 'http://www.nirsoft.net/utils/bluescreenview-x64.zip',
|
||||
'DDU': 'https://www.wagnardsoft.com/DDU/download/DDU%20v18.0.6.8.exe',
|
||||
'ERUNT': 'http://www.aumha.org/downloads/erunt.zip',
|
||||
'Everything32': 'https://www.voidtools.com/Everything-1.4.1.1024.x86.zip',
|
||||
'Everything64': 'https://www.voidtools.com/Everything-1.4.1.1024.x64.zip',
|
||||
'FastCopy': 'https://github.com/FastCopyLab/FastCopyDist2/raw/main/FastCopy5.4.2_installer.exe',
|
||||
'Fluent-Metro': 'https://github.com/bonzibudd/Fluent-Metro/releases/download/v1.5.3/Fluent-Metro_1.5.3.zip',
|
||||
'FurMark': 'https://geeks3d.com/dl/get/728',
|
||||
'HWiNFO': 'https://www.sac.sk/download/utildiag/hwi_764.zip',
|
||||
'LibreOffice32': 'https://download.documentfoundation.org/libreoffice/stable/7.6.2/win/x86/LibreOffice_7.6.2_Win_x86.msi',
|
||||
'LibreOffice64': 'https://download.documentfoundation.org/libreoffice/stable/7.6.2/win/x86_64/LibreOffice_7.6.2_Win_x86-64.msi',
|
||||
'LibreOffice64': 'https://download.documentfoundation.org/libreoffice/stable/7.6.6/win/x86_64/LibreOffice_7.6.6_Win_x86-64.msi',
|
||||
'Linux Reader': 'https://www.diskinternals.com/download/Linux_Reader.exe',
|
||||
'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe',
|
||||
'Neutron': 'http://keir.net/download/neutron.zip',
|
||||
'Notepad++': 'https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v8.5.8/npp.8.5.8.portable.minimalist.7z',
|
||||
'OpenShell': 'https://github.com/Open-Shell/Open-Shell-Menu/releases/download/v4.4.191/OpenShellSetup_4_4_191.exe',
|
||||
'Prime95': 'https://www.mersenne.org/download/software/v30/30.19/p95v3019b13.win64.zip',
|
||||
'PuTTY': 'https://the.earth.li/~sgtatham/putty/latest/w32/putty.zip',
|
||||
'SDIO Torrent': 'https://www.glenn.delahoy.com/downloads/sdio/SDIO_Update.torrent',
|
||||
'ShutUp10': 'https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe',
|
||||
'Windows Repair AIO': 'http://www.tweaking.com/files/setups/tweaking.com_windows_repair_aio.zip',
|
||||
'WinSCP': 'https://sourceforge.net/projects/winscp/files/WinSCP/5.19.5/WinSCP-5.19.5-Portable.zip/download',
|
||||
'WizTree': 'https://diskanalyzer.com/files/wiztree_4_15_portable.zip',
|
||||
'XMPlay': 'https://support.xmplay.com/files/20/xmplay385.zip?v=47090',
|
||||
'XMPlay 7z': 'https://support.xmplay.com/files/16/xmp-7z.zip?v=800962',
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from wk.cfg.main import KIT_NAME_FULL
|
|||
SOURCES = {
|
||||
'Linux': {'Arg': '--linux', 'Type': 'ISO'},
|
||||
'WinPE': {'Arg': '--winpe', 'Type': 'ISO'},
|
||||
'ESET SysRescue': {'Arg': '--eset', 'Type': 'IMG'},
|
||||
'Main Kit': {'Arg': '--main-kit', 'Type': 'KIT'},
|
||||
'Extra Dir': {'Arg': '--extra-dir', 'Type': 'DIR'},
|
||||
}
|
||||
|
|
@ -15,23 +16,44 @@ SOURCES = {
|
|||
# Definitions: Boot entries
|
||||
BOOT_ENTRIES = {
|
||||
# Path to check: Comment to remove
|
||||
'/arch_minimal': 'UFD-MINIMAL',
|
||||
'/casper': 'UFD-ESET',
|
||||
'/sources/boot.wim': 'UFD-WINPE',
|
||||
}
|
||||
BOOT_FILES = {
|
||||
# Directory: extension
|
||||
'/syslinux': 'cfg',
|
||||
'/boot/grub': 'cfg',
|
||||
'/EFI/boot': 'conf',
|
||||
}
|
||||
IMAGE_BOOT_ENTRIES = {
|
||||
'El Capitan': 'UFD-MACOS-10.11',
|
||||
'High Sierra': 'UFD-MACOS-10.13',
|
||||
'Catalina': 'UFD-MACOS-10.15',
|
||||
'3S132': 'UFD-ASD-3S132',
|
||||
'3S138': 'UFD-ASD-3S138',
|
||||
'3S140': 'UFD-ASD-3S140',
|
||||
'3S142A': 'UFD-ASD-3S142A',
|
||||
'3S144': 'UFD-ASD-3S144',
|
||||
'3S145A': 'UFD-ASD-3S145A',
|
||||
'3S146': 'UFD-ASD-3S146',
|
||||
'3S147': 'UFD-ASD-3S147',
|
||||
'3S148': 'UFD-ASD-3S148',
|
||||
'3S150': 'UFD-ASD-3S150',
|
||||
'3S155': 'UFD-ASD-3S155',
|
||||
'3S156': 'UFD-ASD-3S156',
|
||||
'3S162': 'UFD-ASD-3S162',
|
||||
}
|
||||
|
||||
# Definitions: Sources and Destinations
|
||||
## NOTES: Paths are relative to the root of the ISO/UFD
|
||||
## Sources use rsync's trailing slash syntax
|
||||
ITEMS = {
|
||||
'ESET SysRescue': (
|
||||
('/boot/grub/', '/boot/grub/'),
|
||||
('/casper/', '/casper/'),
|
||||
('/EFI/boot/', '/EFI/ESET/'),
|
||||
),
|
||||
'Extra Dir': (
|
||||
('/', '/'),
|
||||
),
|
||||
|
|
@ -42,15 +64,14 @@ ITEMS = {
|
|||
('/', f'/{KIT_NAME_FULL}/'),
|
||||
),
|
||||
'WinPE': (
|
||||
('/bootmgr', '/'),
|
||||
('/BOOTMGR', '/'),
|
||||
('/bootmgr.efi', '/'),
|
||||
('/en_us', '/'),
|
||||
('/Boot/', '/boot/'),
|
||||
('/EFI/Boot/', '/EFI/Microsoft/'),
|
||||
('/EFI/Microsoft/', '/EFI/Microsoft/'),
|
||||
('/Boot/BCD', '/sources/'),
|
||||
('/Boot/boot.sdi', '/sources/'),
|
||||
('/bootmgr', '/sources/'),
|
||||
('/boot/', '/boot/'),
|
||||
('/efi/boot/', '/EFI/Microsoft/'),
|
||||
('/efi/microsoft/', '/EFI/Microsoft/'),
|
||||
('/boot/bcd', '/sources/'),
|
||||
('/boot/boot.sdi', '/sources/'),
|
||||
('/BOOTMGR', '/sources/'),
|
||||
('/sources/boot.wim', '/sources/'),
|
||||
),
|
||||
}
|
||||
|
|
@ -74,6 +95,8 @@ ITEMS_FROM_LIVE = {
|
|||
),
|
||||
}
|
||||
ITEMS_HIDDEN = (
|
||||
# ESET
|
||||
'casper',
|
||||
# Linux (all versions)
|
||||
'arch',
|
||||
'EFI',
|
||||
|
|
@ -83,9 +106,8 @@ ITEMS_HIDDEN = (
|
|||
f'{KIT_NAME_FULL}/.cbin',
|
||||
# WinPE
|
||||
'boot',
|
||||
'bootmgr',
|
||||
'BOOTMGR',
|
||||
'bootmgr.efi',
|
||||
'en-us',
|
||||
'images',
|
||||
'sources',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class BlockPair():
|
|||
self.map_data: dict[str, bool | int] = {}
|
||||
self.map_path: pathlib.Path = pathlib.Path()
|
||||
self.size: int = source_dev.size
|
||||
self.stats = {}
|
||||
self.status: dict[str, float | int | str] = {
|
||||
'read-skip': 'Pending',
|
||||
'read-full': 'Pending',
|
||||
|
|
@ -190,6 +191,25 @@ class BlockPair():
|
|||
# This should never be reached
|
||||
return False
|
||||
|
||||
def reset_progress(self):
|
||||
"""Reset progress to start fresh recovery."""
|
||||
self.map_data = {}
|
||||
self.status = {
|
||||
'read': 'Pending',
|
||||
'trim': 'Pending',
|
||||
'scrape': 'Pending',
|
||||
}
|
||||
self.map_path.write_text(
|
||||
data=cfg.ddrescue.DDRESCUE_MAP_TEMPLATE.format(
|
||||
name=cfg.main.KIT_NAME_FULL,
|
||||
size=self.size,
|
||||
),
|
||||
encoding='utf-8',
|
||||
)
|
||||
|
||||
# Set initial status
|
||||
self.set_initial_status()
|
||||
|
||||
def safety_check(self) -> None:
|
||||
"""Run safety check and abort if necessary."""
|
||||
# TODO: Expand section to support non-Linux systems
|
||||
|
|
@ -249,6 +269,7 @@ def add_clone_block_pairs(state) -> list[hw_disk.Disk]:
|
|||
source_sep = get_partition_separator(state.source.path.name)
|
||||
dest_sep = get_partition_separator(state.destination.path.name)
|
||||
settings = {}
|
||||
source_parts = []
|
||||
|
||||
# Clone settings
|
||||
settings = state.load_settings(discard_unused_settings=True)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import os
|
|||
import pathlib
|
||||
import subprocess
|
||||
|
||||
from typing import Any
|
||||
from random import randint
|
||||
|
||||
import pytz
|
||||
|
|
@ -24,7 +25,7 @@ from wk.hw.smart import (
|
|||
smart_status_ok,
|
||||
update_smart_details,
|
||||
)
|
||||
from wk.ui import ansi, cli
|
||||
from wk.ui import ansi, cli, tmux
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
|
|
@ -129,13 +130,25 @@ def check_destination_health(destination) -> str:
|
|||
result = 'Critical error(s) detected for: {destination.path}'
|
||||
|
||||
# Check for minor errors
|
||||
if not check_attributes(destination, only_blocking=False):
|
||||
if not check_attributes(destination, only_blocking=True):
|
||||
result = f'Attribute error(s) detected for: {destination.path}'
|
||||
|
||||
# Done
|
||||
return result
|
||||
|
||||
|
||||
def detect_drives(state):
|
||||
"""Detect connected drives and check source/dest selections."""
|
||||
cli.clear_screen()
|
||||
cli.print_warning(DETECT_DRIVES_NOTICE)
|
||||
if cli.ask('Are you sure you proceed?'):
|
||||
cli.print_standard('Forcing controllers to rescan for devices...')
|
||||
cmd = 'echo "- - -" | sudo tee /sys/class/scsi_host/host*/scan'
|
||||
exe.run_program(cmd, check=False, shell=True)
|
||||
if source_or_destination_changed(state):
|
||||
cli.abort()
|
||||
|
||||
|
||||
def generate_test_map(map_path: pathlib.Path, size: int) -> None:
|
||||
"""Generate test map with roughly 20% of the space marked as bad."""
|
||||
chunk = 2*1024**2
|
||||
|
|
@ -176,9 +189,28 @@ def get_ddrescue_settings(settings_menu) -> list:
|
|||
return settings
|
||||
|
||||
|
||||
def get_stats(output: str | None = None) -> dict[str, Any]:
|
||||
"""Get stats from ddrescue output, returns dict."""
|
||||
output = tmux.capture_pane()
|
||||
stats = {}
|
||||
temp = []
|
||||
for line in output[output.find('ipos:'):].splitlines():
|
||||
temp.extend(line.split(','))
|
||||
for line in temp:
|
||||
line = line.strip()
|
||||
try:
|
||||
key, value = line.split(':')
|
||||
stats[key] = value.strip()
|
||||
except ValueError:
|
||||
# ignore
|
||||
pass
|
||||
return stats
|
||||
|
||||
|
||||
def finalize_recovery(state: State, dry_run: bool = True) -> None:
|
||||
"""Show recovery finalization options and run selected functions."""
|
||||
options = (
|
||||
'Post results to osTicket',
|
||||
'Relocate Backup GPT',
|
||||
'Zero-fill Gaps',
|
||||
'Zero-fill Extra Space',
|
||||
|
|
@ -221,6 +253,9 @@ def finalize_recovery(state: State, dry_run: bool = True) -> None:
|
|||
if state.mode == 'Image':
|
||||
details['Disabled'] = True
|
||||
details['Selected'] = False
|
||||
if 'osTicket' in name:
|
||||
# This is last to override the Image section above
|
||||
details['Selected'] = not state.ost.disabled
|
||||
menu.add_option(name, details)
|
||||
|
||||
# Show menu
|
||||
|
|
@ -240,6 +275,10 @@ def finalize_recovery(state: State, dry_run: bool = True) -> None:
|
|||
# NOTE: This needs to be run last to avoid corrupting/erasing the backup GPT
|
||||
relocate_backup_gpt(state, dry_run=dry_run)
|
||||
|
||||
# osTicket
|
||||
if menu.options['Post results to osTicket']['Selected']:
|
||||
state.post_to_osticket()
|
||||
|
||||
|
||||
def is_missing_source_or_destination(state) -> bool:
|
||||
"""Check if source or destination dissapeared, returns bool."""
|
||||
|
|
@ -292,6 +331,9 @@ def main() -> None:
|
|||
|
||||
# Init
|
||||
state = State(log_dir=log_path.parent)
|
||||
if not args['--force-local-map']:
|
||||
state.ost.select_ticket()
|
||||
state.update_top_panes()
|
||||
try:
|
||||
state.init_recovery(args)
|
||||
except (FileNotFoundError, std.GenericAbort):
|
||||
|
|
@ -299,7 +341,7 @@ def main() -> None:
|
|||
cli.abort()
|
||||
|
||||
# Show menu
|
||||
main_menu = menus.main()
|
||||
main_menu = menus.main(ost_disabled=state.ost.disabled)
|
||||
settings_menu = menus.settings(state.mode)
|
||||
while True:
|
||||
selection = main_menu.advanced_select()
|
||||
|
|
@ -316,14 +358,18 @@ def main() -> None:
|
|||
|
||||
# Detect drives
|
||||
if 'Detect drives' in selection[0]:
|
||||
cli.clear_screen()
|
||||
cli.print_warning(DETECT_DRIVES_NOTICE)
|
||||
if cli.ask('Are you sure you proceed?'):
|
||||
cli.print_standard('Forcing controllers to rescan for devices...')
|
||||
cmd = 'echo "- - -" | sudo tee /sys/class/scsi_host/host*/scan'
|
||||
exe.run_program([cmd], check=False, shell=True)
|
||||
if source_or_destination_changed(state):
|
||||
cli.abort()
|
||||
detect_drives(state)
|
||||
|
||||
# Tech note
|
||||
if 'tech note' in selection[0]:
|
||||
note_lines = state.ost.add_note(
|
||||
'Please enter any additional information about this recovery',
|
||||
)
|
||||
state.update_top_panes(note_lines=note_lines)
|
||||
|
||||
# Start over
|
||||
if 'Fresh start' in selection[0]:
|
||||
state.fresh_start()
|
||||
|
||||
# Start recovery
|
||||
if 'Start' in selection:
|
||||
|
|
@ -379,6 +425,9 @@ def relocate_backup_gpt(state: State, dry_run: bool = True) -> None:
|
|||
if proc.returncode:
|
||||
cli.print_error('ERROR: Failed to relocate backup GPT.')
|
||||
LOG.error('sfdisk result: %s, %s', proc.stdout, proc.stderr)
|
||||
state.notes.add('WARNING: Failed to relocated backup GPT')
|
||||
else:
|
||||
state.notes.add('NOTE: Relocated backup GPT to the end of the disk.')
|
||||
|
||||
|
||||
def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None:
|
||||
|
|
@ -448,6 +497,10 @@ def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None:
|
|||
LOG.info('ddrescue cmd: %s', cmd)
|
||||
return
|
||||
|
||||
# Stats
|
||||
if pass_name not in block_pair.stats:
|
||||
block_pair.stats[pass_name] = {}
|
||||
|
||||
# Start ddrescue and ddrescueview (if enabled)
|
||||
proc = exe.popen_program(cmd)
|
||||
if (
|
||||
|
|
@ -476,6 +529,7 @@ def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None:
|
|||
_i += 1
|
||||
|
||||
# Update progress
|
||||
block_pair.stats[pass_name].update(get_stats())
|
||||
block_pair.update_progress(pass_name)
|
||||
state.update_progress_pane('Active')
|
||||
|
||||
|
|
@ -709,9 +763,17 @@ def zero_fill_gaps(
|
|||
|
||||
# Re-run ddrescue to zero-fill gaps
|
||||
proc = exe.run_program(cmd, check=False, pipe=False)
|
||||
LOG.info('Zero-fill result: %s', proc)
|
||||
if proc.returncode:
|
||||
cli.print_error('ERROR: Failed to zero-fill: {block_pair.destination}')
|
||||
LOG.error('zero-fill error: %s, %s', proc.stdout, proc.stderr)
|
||||
state.notes.add('WARNING: Failed to zero-fill destination')
|
||||
else:
|
||||
msg = (
|
||||
'NOTE: Zero-filled gaps'
|
||||
f'{" and extra space on destination." if extend_to_end else "."}'
|
||||
)
|
||||
state.notes.add(msg)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ if PLATFORM == 'Darwin':
|
|||
DDRESCUE_SETTINGS['Default']['--odirect'] = {'Selected': False, 'Hidden': True}
|
||||
MENU_ACTIONS = (
|
||||
'Start',
|
||||
'Add tech note',
|
||||
f'Change settings {ansi.color_string("(experts only)", "YELLOW")}',
|
||||
f'Detect drives {ansi.color_string("(experts only)", "YELLOW")}',
|
||||
f'Fresh start {ansi.color_string("(experts only)", "YELLOW")}',
|
||||
'Quit')
|
||||
MENU_TOGGLES = {
|
||||
'Auto continue (if recovery % over threshold)': True,
|
||||
|
|
@ -44,7 +46,7 @@ SETTING_PRESETS = (
|
|||
|
||||
|
||||
# Functions
|
||||
def main() -> cli.Menu:
|
||||
def main(ost_disabled: bool) -> cli.Menu:
|
||||
"""Main menu, returns wk.ui.cli.Menu."""
|
||||
menu = cli.Menu(title=ansi.color_string('ddrescue TUI: Main Menu', 'GREEN'))
|
||||
menu.separator = ' '
|
||||
|
|
@ -56,6 +58,15 @@ def main() -> cli.Menu:
|
|||
for toggle, selected in MENU_TOGGLES.items():
|
||||
menu.add_toggle(toggle, {'Selected': selected})
|
||||
|
||||
# osTicket actions
|
||||
if ost_disabled:
|
||||
menu.actions['Add tech note']['Disabled'] = True
|
||||
menu.actions['Add tech note']['Hidden'] = True
|
||||
# TODO: Remove this ugly call
|
||||
menu.actions[MENU_ACTIONS[2]]['Separator'] = True
|
||||
else:
|
||||
menu.actions['Add tech note']['Separator'] = True
|
||||
|
||||
# Done
|
||||
return menu
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from typing import Any
|
|||
import psutil
|
||||
import pytz
|
||||
|
||||
from wk import cfg, debug, exe, io, net, std
|
||||
from wk import cfg, debug, exe, io, net, osticket, std
|
||||
from wk.clone import menus
|
||||
from wk.clone.block_pair import (
|
||||
BlockPair,
|
||||
|
|
@ -81,8 +81,10 @@ class State():
|
|||
self.block_pairs: list[BlockPair] = []
|
||||
self.destination: hw_disk.Disk | pathlib.Path = pathlib.Path('/dev/null')
|
||||
self.log_dir: pathlib.Path = log_dir
|
||||
self.ost = osticket.osTicket()
|
||||
self.progress_out: pathlib.Path = self.log_dir.joinpath('progress.out')
|
||||
self.mode: str = '?'
|
||||
self.notes: set = set()
|
||||
self.source: hw_disk.Disk | None = None
|
||||
self.working_dir: pathlib.Path | None = None
|
||||
self.ui: tui.TUI = tui.TUI('Source')
|
||||
|
|
@ -145,7 +147,7 @@ class State():
|
|||
errors_detected = True
|
||||
|
||||
# Check for minor errors
|
||||
if not check_attributes(self.destination, only_blocking=False):
|
||||
if not check_attributes(self.destination, only_blocking=True):
|
||||
cli.print_warning(
|
||||
f'Attribute error(s) detected for: {self.destination.path}',
|
||||
)
|
||||
|
|
@ -250,18 +252,45 @@ class State():
|
|||
if not cli.ask(prompt_msg):
|
||||
raise std.GenericAbort()
|
||||
|
||||
def fresh_start(self) -> None:
|
||||
"""Clean working dir and reset progress."""
|
||||
cli.print_error(
|
||||
'This will reset all progress and create new map file(s).',
|
||||
)
|
||||
cli.print_warning(
|
||||
"Please only proceed if you understand what you're doing!",
|
||||
)
|
||||
cli.print_warning(
|
||||
'NOTE: This will keep the current partition selection(s).',
|
||||
)
|
||||
if not cli.ask('Continue?'):
|
||||
return
|
||||
|
||||
# Clean and reset
|
||||
clean_working_dir(self.working_dir)
|
||||
for block_pair in self.block_pairs:
|
||||
block_pair.reset_progress()
|
||||
self.update_progress_pane('Idle')
|
||||
|
||||
def generate_report(self) -> list[str]:
|
||||
"""Generate report of overall and per block_pair results, returns list."""
|
||||
report = []
|
||||
stats_str = (
|
||||
'... pass: {pass_name} -- '
|
||||
'non-trimmed: {non-trimmed}, '
|
||||
'non-scraped: {non-scraped}, '
|
||||
'bad-sectors: {bad-sector}, '
|
||||
'slow reads: {slow reads}, '
|
||||
'run time: {run time}'
|
||||
)
|
||||
|
||||
# Header
|
||||
report.append(f'{self.mode.title()} Results:')
|
||||
report.append(' ')
|
||||
report.append(f'Source: {self.source.description}')
|
||||
report.append(f'... Source: {self.source.description}')
|
||||
if self.mode == 'Clone':
|
||||
report.append(f'Destination: {self.destination.description}')
|
||||
report.append(f'... Destination: {self.destination.description}')
|
||||
else:
|
||||
report.append(f'Destination: {self.destination}/')
|
||||
report.append(f'... Destination: {self.destination}/')
|
||||
|
||||
# Overall
|
||||
report.append(' ')
|
||||
|
|
@ -274,8 +303,17 @@ class State():
|
|||
report.append(f'Overall rescued: {percent}, error size: {error_size_str}')
|
||||
|
||||
# Block-Pairs
|
||||
if len(self.block_pairs) > 1:
|
||||
report.append(' ')
|
||||
if len(self.block_pairs) == 1:
|
||||
stats = self.block_pairs[0].stats
|
||||
if stats:
|
||||
try:
|
||||
for _name, _stats in stats.items():
|
||||
report.append(stats_str.format(pass_name=_name, **_stats))
|
||||
except KeyError:
|
||||
# Ignore and omit stats
|
||||
pass
|
||||
else:
|
||||
# Two or more block_pairs
|
||||
for pair in self.block_pairs:
|
||||
error_size = pair.get_error_size()
|
||||
error_size_str = std.bytes_to_string(error_size, decimals=2)
|
||||
|
|
@ -284,11 +322,26 @@ class State():
|
|||
pair_size = std.bytes_to_string(pair.size, decimals=2)
|
||||
percent = pair.get_percent_recovered()
|
||||
percent = format_status_string(percent, width=0)
|
||||
report.append(' ')
|
||||
report.append(
|
||||
f'{pair.source.name} ({pair_size}) '
|
||||
f'rescued: {percent}, '
|
||||
f'error size: {error_size_str}'
|
||||
)
|
||||
stats = pair.stats
|
||||
if not stats:
|
||||
continue
|
||||
try:
|
||||
for _name, _stats in stats.items():
|
||||
report.append(stats_str.format(pass_name=_name, **_stats))
|
||||
except KeyError:
|
||||
# Ignore and omit stats
|
||||
pass
|
||||
|
||||
# Notes
|
||||
if self.notes:
|
||||
report.append(' ')
|
||||
report.extend(sorted(self.notes))
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
|
@ -358,10 +411,17 @@ class State():
|
|||
self.destination.update_details(skip_children=False)
|
||||
|
||||
# Confirmation #1
|
||||
self.confirm_selections(
|
||||
prompt_msg='Are these selections correct?',
|
||||
source_parts=source_parts,
|
||||
)
|
||||
advanced_selection = False
|
||||
try:
|
||||
self.confirm_selections(
|
||||
prompt_msg='Are these selections correct? (use "No" for advanced selection)',
|
||||
source_parts=source_parts,
|
||||
)
|
||||
except std.GenericAbort:
|
||||
if cli.ask('Proceed to advanced partition selection?'):
|
||||
advanced_selection = True
|
||||
else:
|
||||
raise
|
||||
|
||||
# Update panes
|
||||
self.update_progress_pane('Idle')
|
||||
|
|
@ -371,6 +431,8 @@ class State():
|
|||
self.mode,
|
||||
self.destination,
|
||||
force_local=docopt_args['--force-local-map'],
|
||||
ticket_id=self.ost.ticket_id,
|
||||
ticket_name=self.ost.ticket_name,
|
||||
)
|
||||
|
||||
# Start fresh if requested
|
||||
|
|
@ -378,10 +440,24 @@ class State():
|
|||
clean_working_dir(self.working_dir)
|
||||
|
||||
# Add block pairs
|
||||
if self.mode == 'Clone':
|
||||
source_parts = add_clone_block_pairs(self)
|
||||
if advanced_selection:
|
||||
if self.mode == 'Clone':
|
||||
source_parts = add_clone_block_pairs(self)
|
||||
else:
|
||||
source_parts = add_image_block_pairs(self)
|
||||
else:
|
||||
source_parts = add_image_block_pairs(self)
|
||||
if self.mode == 'Clone':
|
||||
source_parts.append(hw_disk.Disk(self.source.path))
|
||||
self.add_block_pair(
|
||||
hw_disk.Disk(self.source.path),
|
||||
pathlib.Path(self.destination.path),
|
||||
)
|
||||
if self.mode == 'Image':
|
||||
source_parts.append(hw_disk.Disk(self.source.path))
|
||||
self.add_block_pair(
|
||||
hw_disk.Disk(self.source.path),
|
||||
self.destination,
|
||||
)
|
||||
|
||||
# Update SMART data
|
||||
## TODO: Verify if needed
|
||||
|
|
@ -396,7 +472,8 @@ class State():
|
|||
|
||||
# Confirmation #2
|
||||
self.update_progress_pane('Idle')
|
||||
self.confirm_selections('Start recovery?', source_parts)
|
||||
if advanced_selection:
|
||||
self.confirm_selections('Start recovery?', source_parts)
|
||||
|
||||
# Unmount source and/or destination under macOS
|
||||
if PLATFORM == 'Darwin':
|
||||
|
|
@ -496,6 +573,29 @@ class State():
|
|||
"""Check if all block_pairs completed pass_name, returns bool."""
|
||||
return all(p.pass_complete(pass_name) for p in self.block_pairs)
|
||||
|
||||
def post_to_osticket(self):
|
||||
"""Post results to osTicket."""
|
||||
color = 'Diags'
|
||||
percent = self.get_percent_recovered()
|
||||
report = self.generate_report()
|
||||
report[0] = f'ddrescue-tui {report[0]}'
|
||||
if percent < 90:
|
||||
color = 'Diags FAIL'
|
||||
elif percent < 95:
|
||||
color = 'Normal'
|
||||
|
||||
# Init osTicket if necessary
|
||||
if not self.ost.ticket_id:
|
||||
self.ost.init()
|
||||
self.ost.select_ticket()
|
||||
|
||||
# Bail if user changed their mind
|
||||
if not self.ost.ticket_id:
|
||||
return
|
||||
|
||||
# Post report
|
||||
self.ost.post_response('\n'.join(report), color=color)
|
||||
|
||||
def retry_all_passes(self) -> None:
|
||||
"""Prep block_pairs for a retry recovery attempt."""
|
||||
LOG.warning('Updating block_pairs for retry')
|
||||
|
|
@ -612,8 +712,10 @@ class State():
|
|||
# Write to progress file
|
||||
self.progress_out.write_text('\n'.join(report), encoding='utf-8', errors='ignore')
|
||||
|
||||
def update_top_panes(self) -> None:
|
||||
def update_top_panes(self, note_lines: list | None = None) -> None:
|
||||
"""(Re)create top source/destination panes."""
|
||||
if not note_lines:
|
||||
note_lines = []
|
||||
source_exists = True
|
||||
source_str = ''
|
||||
dest_exists = True
|
||||
|
|
@ -681,6 +783,29 @@ class State():
|
|||
dest_str,
|
||||
)
|
||||
|
||||
# Bail if ticket not selected
|
||||
if not self.ost:
|
||||
return
|
||||
|
||||
# Ticket Details
|
||||
self.ui.reset_subtitle_pane()
|
||||
if self.ost.ticket_id and not self.ui.layout['Subtitle']['Panes']:
|
||||
self.ui.add_subtitle_pane(
|
||||
ansi.color_string(
|
||||
[f'#{self.ost.ticket_id}', str(self.ost.ticket_name)],
|
||||
[None, 'CYAN'],
|
||||
),
|
||||
str(self.ost.ticket_subject),
|
||||
)
|
||||
|
||||
# Tech note
|
||||
note_lines = self.ost.note.replace('...', '').splitlines()
|
||||
if note_lines:
|
||||
self.ui.add_subtitle_pane(
|
||||
ansi.color_string('Tech Note', 'YELLOW'),
|
||||
' | '.join(note_lines),
|
||||
)
|
||||
|
||||
|
||||
# Functions
|
||||
def build_directory_report(path: pathlib.Path) -> list[str]:
|
||||
|
|
@ -801,7 +926,7 @@ def build_object_report(obj) -> list[str]:
|
|||
return report
|
||||
|
||||
|
||||
def clean_working_dir(working_dir) -> None:
|
||||
def clean_working_dir(working_dir, confirm=False) -> None:
|
||||
"""Clean working directory to ensure a fresh recovery session.
|
||||
|
||||
NOTE: Data from previous sessions will be preserved
|
||||
|
|
@ -814,6 +939,9 @@ def clean_working_dir(working_dir) -> None:
|
|||
# Move settings, maps, etc to backup_dir
|
||||
for entry in os.scandir(working_dir):
|
||||
if entry.name.endswith(('.dd', '.json', '.map')):
|
||||
if confirm and entry.name.endswith('.json'):
|
||||
# Keep JSON settings if using the menu option
|
||||
continue
|
||||
new_path = f'{backup_dir}/{entry.name}'
|
||||
new_path = io.non_clobber_path(new_path)
|
||||
shutil.move(entry.path, new_path)
|
||||
|
|
@ -953,11 +1081,22 @@ def get_percent_color(percent) -> str:
|
|||
return color
|
||||
|
||||
|
||||
def get_working_dir(mode, destination, force_local=False) -> pathlib.Path:
|
||||
def get_working_dir(
|
||||
mode,
|
||||
destination,
|
||||
force_local=False,
|
||||
ticket_id=None,
|
||||
ticket_name=None,
|
||||
) -> pathlib.Path:
|
||||
"""Get working directory using mode and destination, returns path."""
|
||||
ticket_id = cli.get_ticket_id()
|
||||
working_dir = None
|
||||
|
||||
# Set ticket ID
|
||||
if ticket_id is None:
|
||||
now = datetime.datetime.now(tz=TIMEZONE)
|
||||
ticket_id = now.strftime('%Y-%m-%dT%H%M_%Z')
|
||||
ticket_id = ticket_id.replace(' ', '_')
|
||||
|
||||
# Use preferred path if possible
|
||||
if mode == 'Image':
|
||||
try:
|
||||
|
|
@ -988,6 +1127,12 @@ def get_working_dir(mode, destination, force_local=False) -> pathlib.Path:
|
|||
if mode == 'Clone':
|
||||
working_dir = working_dir.joinpath(ticket_id)
|
||||
|
||||
# Append ticket name if set
|
||||
if ticket_name:
|
||||
working_dir = working_dir.with_name(
|
||||
f'{ticket_id}_{ticket_name.replace(" ", "-")}',
|
||||
)
|
||||
|
||||
# Create directory
|
||||
working_dir.mkdir(parents=True, exist_ok=True)
|
||||
os.chdir(working_dir)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ def build_cmd_kwargs(
|
|||
cmd: list[str],
|
||||
minimized: bool = False,
|
||||
pipe: bool = True,
|
||||
priority: bool = False,
|
||||
shell: bool = False,
|
||||
**kwargs) -> dict[str, Any]:
|
||||
"""Build kwargs for use by subprocess functions, returns dict.
|
||||
|
|
@ -119,6 +120,9 @@ def build_cmd_kwargs(
|
|||
startupinfo.wShowWindow = 6
|
||||
cmd_kwargs['startupinfo'] = startupinfo
|
||||
|
||||
# High priority
|
||||
if priority:
|
||||
cmd_kwargs['creationflags'] = subprocess.HIGH_PRIORITY_CLASS
|
||||
|
||||
# Pipe output
|
||||
if pipe:
|
||||
|
|
@ -268,6 +272,18 @@ def run_program(
|
|||
return proc
|
||||
|
||||
|
||||
def set_proc_priority(name, priority, exact=True):
|
||||
"""Set process priority by name.
|
||||
|
||||
NOTE: priority currently can be only set to NORMAL or HIGH.
|
||||
"""
|
||||
if priority not in ('NORMAL', 'HIGH'):
|
||||
raise RuntimeError('Invalid process priority specified.')
|
||||
|
||||
for proc in get_procs(name, exact=exact):
|
||||
proc.nice(psutil.HIGH_PRIORITY_CLASS)
|
||||
|
||||
|
||||
def start_thread(
|
||||
function: Callable,
|
||||
args: Iterable[Any] | None = None,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,25 @@
|
|||
"""WizardKit: Graph Functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import pathlib
|
||||
import time
|
||||
|
||||
from matplotlib import pyplot
|
||||
import requests
|
||||
|
||||
from wk.cfg.net import BENCHMARK_SERVER, IMGUR_CLIENT_ID
|
||||
from wk.ui import ansi
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
GRAPH_FIGURE_SIZE = (
|
||||
9.167, # 660px at 72dpi
|
||||
4.167, # 300px at 72dpi
|
||||
)
|
||||
GRAPH_HORIZONTAL = ('▁', '▂', '▃', '▄', '▅', '▆', '▇', '█')
|
||||
GRAPH_VERTICAL = (
|
||||
'▏', '▎', '▍', '▌',
|
||||
|
|
@ -33,6 +45,137 @@ THRESH_GREAT = 750 * 1024**2
|
|||
|
||||
|
||||
# Functions
|
||||
def reduce_cpu_temp_list(list_of_temps, factor=5, start=30) -> list[float]:
|
||||
"""Reduce temperature list by averaging adjacent values.
|
||||
|
||||
NOTE: This only averages values after the amount defined by start.
|
||||
NOTE 2: If the last group is less than the factor it is simply dropped.
|
||||
"""
|
||||
new_list = list_of_temps[:start]
|
||||
list_of_temps = list_of_temps[start:]
|
||||
while list_of_temps:
|
||||
group = list_of_temps[:factor]
|
||||
list_of_temps = list_of_temps[factor:]
|
||||
if len(group) < factor:
|
||||
continue
|
||||
new_list.append(sum(group)/factor)
|
||||
return new_list
|
||||
|
||||
|
||||
def export_cpu_graph(cpu_description, log_dir, sensor_history):
|
||||
"""Exports PNG graph using matplotlib."""
|
||||
lines = {}
|
||||
offset_labels = []
|
||||
out_path = pathlib.Path(f'{log_dir}/cpu_tests.png')
|
||||
run_averages = {}
|
||||
|
||||
# Safety check
|
||||
if not sensor_history:
|
||||
raise RuntimeError('No sensor_data available.')
|
||||
|
||||
# Prep
|
||||
offset = 0
|
||||
for run_label, sensor_data in sensor_history:
|
||||
all_run_temps = []
|
||||
offset_labels.append((offset, run_label))
|
||||
run_length = 0
|
||||
|
||||
for adapter in sensor_data.get('CPUTemps', {}).values():
|
||||
for source, data in adapter.items():
|
||||
y_values = data['Temps']
|
||||
if run_label in ('Sysbench', 'Prime95'):
|
||||
y_values = reduce_cpu_temp_list(y_values)
|
||||
all_run_temps.extend(y_values)
|
||||
run_length = max(run_length, len(y_values))
|
||||
if source not in lines:
|
||||
lines[source] = []
|
||||
lines[source].extend(y_values)
|
||||
try:
|
||||
run_averages[run_label] = {
|
||||
'Start': offset,
|
||||
'End': offset+run_length,
|
||||
'Temp': sum(all_run_temps) / len(all_run_temps),
|
||||
}
|
||||
except ZeroDivisionError:
|
||||
# Ignore
|
||||
pass
|
||||
offset += run_length
|
||||
|
||||
# Build graph
|
||||
_, ax = pyplot.subplots(
|
||||
dpi=72,
|
||||
figsize=list(x*2 for x in GRAPH_FIGURE_SIZE),
|
||||
layout='constrained',
|
||||
)
|
||||
ax.set_title(cpu_description)
|
||||
ax.set_xticks([])
|
||||
for label, data in lines.items():
|
||||
ax.plot(data, label=label)
|
||||
#prev_label = 'Idle' # Always skip Idle
|
||||
prev_label = ''
|
||||
for offset, label in offset_labels:
|
||||
color = 'grey'
|
||||
if label == prev_label:
|
||||
continue
|
||||
if label == 'Sysbench':
|
||||
color = 'orange'
|
||||
if label == 'Prime95':
|
||||
color = 'red'
|
||||
if label == 'Cooldown':
|
||||
color = 'blue'
|
||||
label = ''
|
||||
ax.axvline(x=offset, color=color, label=label, linestyle='--')
|
||||
#ax.axvline(x=offset, color=color)
|
||||
prev_label = label
|
||||
for run_label, data in run_averages.items():
|
||||
if run_label not in ('Prime95', 'Sysbench'):
|
||||
continue
|
||||
ax.axhline(
|
||||
y = data['Temp'],
|
||||
color = 'orange' if run_label == 'Sysbench' else 'red',
|
||||
label = f'{run_label} (Avg)',
|
||||
linestyle = ':',
|
||||
)
|
||||
ax.legend()
|
||||
pyplot.savefig(out_path)
|
||||
|
||||
# Done
|
||||
return out_path
|
||||
|
||||
|
||||
def export_io_graph(disk, log_dir, read_rates):
|
||||
"""Exports PNG graph using matplotlib."""
|
||||
|
||||
# Safety check
|
||||
if not read_rates:
|
||||
raise RuntimeError(f'No read rates for {disk.path}')
|
||||
|
||||
# Prep
|
||||
max_rate = max(read_rates) / (1024**2)
|
||||
max_rate = max(800, max_rate)
|
||||
out_path = pathlib.Path(f'{log_dir}/{disk.path.name}_iobenchmark.png')
|
||||
plot_data = ([], [])
|
||||
|
||||
# Prep data for graph
|
||||
for i, rate in enumerate(read_rates):
|
||||
plot_data[0].append((i+1) / len(read_rates) * 100) # Step
|
||||
plot_data[1].append(int(rate / (1024**2))) # Data
|
||||
|
||||
# Build graph
|
||||
_, ax = pyplot.subplots(
|
||||
dpi=72,
|
||||
figsize=GRAPH_FIGURE_SIZE,
|
||||
layout='constrained',
|
||||
)
|
||||
ax.set_title('I/O Benchmark')
|
||||
ax.plot(*plot_data, label=disk.description.replace('_', ' '))
|
||||
ax.legend()
|
||||
pyplot.savefig(out_path)
|
||||
|
||||
# Done
|
||||
return out_path
|
||||
|
||||
|
||||
def generate_horizontal_graph(
|
||||
rate_list: list[float],
|
||||
graph_width: int = 40,
|
||||
|
|
@ -116,6 +259,76 @@ def merge_rates(
|
|||
return merged_rates
|
||||
|
||||
|
||||
def upload_to_imgur(image_path):
|
||||
"""Upload image to Imgur and return image url as str."""
|
||||
image_data = None
|
||||
image_link = None
|
||||
|
||||
# Bail early
|
||||
if not image_path:
|
||||
raise RuntimeError(f'Invalid image path: {image_path}')
|
||||
|
||||
# Read image file and convert to base64 then convert to str
|
||||
with open(image_path, 'rb') as _f:
|
||||
image_data = base64.b64encode(_f.read()).decode()
|
||||
|
||||
# POST image
|
||||
url = 'https://api.imgur.com/3/image'
|
||||
boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW'
|
||||
payload = (
|
||||
f'--{boundary}\r\nContent-Disposition: form-data; '
|
||||
f'name="image"\r\n\r\n{image_data}\r\n--{boundary}--'
|
||||
)
|
||||
headers = {
|
||||
'content-type': f'multipart/form-data; boundary={boundary}',
|
||||
'Authorization': f'Client-ID {IMGUR_CLIENT_ID}',
|
||||
}
|
||||
response = requests.request(
|
||||
'POST',
|
||||
url,
|
||||
data=payload,
|
||||
headers=headers,
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
# Return image link
|
||||
if response.ok:
|
||||
json_data = json.loads(response.text)
|
||||
image_link = json_data['data']['link']
|
||||
return image_link
|
||||
|
||||
|
||||
def upload_to_nextcloud(image_path, ticket_number, dev_name):
|
||||
"""Upload image to Nextcloud server and return folder url as str."""
|
||||
image_data = None
|
||||
ticket_range = f'{ticket_number[:3]}00-{ticket_number[:3]}99'
|
||||
|
||||
# Bail early
|
||||
if not image_path:
|
||||
raise RuntimeError(f'Invalid image path: {image_path}')
|
||||
|
||||
# Read image file and convert to base64
|
||||
with open(image_path, 'rb') as _f:
|
||||
image_data = _f.read()
|
||||
|
||||
# PUT image
|
||||
url = (
|
||||
f'{BENCHMARK_SERVER["Url"]}/'
|
||||
f'{ticket_range}/{ticket_number}_iobenchmark'
|
||||
f'_{dev_name}_{time.strftime("%Y-%m-%d_%H%M_%z")}.png'
|
||||
)
|
||||
requests.put(
|
||||
url,
|
||||
data=image_data,
|
||||
auth=(BENCHMARK_SERVER['User'], BENCHMARK_SERVER['Pass']),
|
||||
headers={'X-Requested-With': 'XMLHttpRequest'},
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
# Return folder link
|
||||
return BENCHMARK_SERVER['Short Url']
|
||||
|
||||
|
||||
def vertical_graph_line(percent: float, rate: float, scale: int = 32) -> str:
|
||||
"""Build colored graph string using thresholds, returns str."""
|
||||
color_bar = None
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ from . import diags
|
|||
from . import disk
|
||||
from . import keyboard
|
||||
from . import network
|
||||
from . import osticket
|
||||
from . import screensavers
|
||||
from . import sensors
|
||||
from . import smart
|
||||
from . import surface_scan
|
||||
from . import system
|
||||
from . import test
|
||||
from . import volumes
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
import platform
|
||||
|
||||
from subprocess import PIPE, STDOUT
|
||||
|
||||
from wk import graph
|
||||
from wk.cfg.hw import (
|
||||
IO_ALT_TEST_SIZE_FACTOR,
|
||||
IO_BLOCK_SIZE,
|
||||
|
|
@ -24,6 +24,9 @@ from wk.exe import run_program
|
|||
from wk.std import PLATFORM
|
||||
from wk.ui import ansi
|
||||
|
||||
if platform.system() != 'Windows':
|
||||
from wk import graph
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
|
@ -97,7 +100,7 @@ def calc_io_dd_values(dev_size, test_mode=False) -> dict[str, int]:
|
|||
}
|
||||
|
||||
|
||||
def check_io_results(test_obj, rate_list, graph_width) -> None:
|
||||
def check_io_results(state, test_obj, rate_list, graph_width) -> None:
|
||||
"""Check I/O restuls and generate report using rate_list."""
|
||||
avg_read = sum(rate_list) / len(rate_list)
|
||||
min_read = min(rate_list)
|
||||
|
|
@ -141,8 +144,36 @@ def check_io_results(test_obj, rate_list, graph_width) -> None:
|
|||
else:
|
||||
test_obj.set_status('Unknown')
|
||||
|
||||
# Export and upload graphs
|
||||
export_and_upload_graphs(state, test_obj, rate_list)
|
||||
|
||||
def run_io_test(test_obj, log_path, test_mode=False) -> None:
|
||||
|
||||
def export_and_upload_graphs(state, test_obj, rate_list):
|
||||
"""Export and upload graphs."""
|
||||
image_path = None
|
||||
try:
|
||||
image_path = graph.export_io_graph(test_obj.dev, state.log_dir, rate_list)
|
||||
except RuntimeError as err:
|
||||
# Failed to export PNG, skip uploads below
|
||||
LOG.error('Failed to export graph: %s', err)
|
||||
test_obj.report.append('Failed to export graph')
|
||||
return
|
||||
|
||||
# Upload PNG
|
||||
if not state.ost.disabled and state.ost.ticket_id:
|
||||
try:
|
||||
imgur_url = graph.upload_to_imgur(image_path)
|
||||
nextcloud_url = graph.upload_to_nextcloud(
|
||||
image_path, state.ost.ticket_id, test_obj.dev.path.name)
|
||||
test_obj.report.append(f'Imgur: {imgur_url}')
|
||||
test_obj.report.append(f'Nextcloud: {nextcloud_url}')
|
||||
except Exception as err:
|
||||
LOG.error('%s', err)
|
||||
LOG.error('Failed to upload graph')
|
||||
test_obj.report.append('Failed to upload graph')
|
||||
|
||||
|
||||
def run_io_test(state, test_obj, log_path, test_mode=False) -> None:
|
||||
"""Run I/O benchmark and handle exceptions."""
|
||||
dev_path = test_obj.dev.path
|
||||
if PLATFORM == 'Darwin':
|
||||
|
|
@ -211,7 +242,7 @@ def run_io_test(test_obj, log_path, test_mode=False) -> None:
|
|||
offset += dd_values['Read Blocks'] + skip
|
||||
|
||||
# Check results
|
||||
check_io_results(test_obj, read_rates, IO_GRAPH_WIDTH)
|
||||
check_io_results(state, test_obj, read_rates, IO_GRAPH_WIDTH)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
|
|
@ -13,6 +14,13 @@ from wk.os.mac import set_fans as macos_set_fans
|
|||
from wk.std import PLATFORM
|
||||
from wk.ui import ansi
|
||||
|
||||
if platform.system() != 'Windows':
|
||||
from wk.graph import (
|
||||
export_cpu_graph,
|
||||
upload_to_imgur,
|
||||
upload_to_nextcloud,
|
||||
)
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
|
@ -20,8 +28,9 @@ SysbenchType = tuple[subprocess.Popen, TextIO]
|
|||
|
||||
|
||||
# Functions
|
||||
def check_cooling_results(sensors, test_object) -> None:
|
||||
def check_cooling_results(state, test_object) -> None:
|
||||
"""Check cooling result via sensor data."""
|
||||
sensors = state.sensors
|
||||
idle_temp = sensors.get_cpu_temp('Idle')
|
||||
cooldown_temp = sensors.get_cpu_temp('Cooldown')
|
||||
max_temp = sensors.get_cpu_temp('Max')
|
||||
|
|
@ -84,6 +93,37 @@ def check_cooling_results(sensors, test_object) -> None:
|
|||
*report_labels, only_cpu=True, include_avg_for=average_labels):
|
||||
test_object.report.append(f' {line}')
|
||||
|
||||
# Export and upload graph
|
||||
export_and_upload_graphs(state, test_object)
|
||||
|
||||
|
||||
def export_and_upload_graphs(state, test_object):
|
||||
"""Export and upload graphs."""
|
||||
image_path = None
|
||||
try:
|
||||
image_path = export_cpu_graph(
|
||||
cpu_description = state.system.cpu_description,
|
||||
log_dir = state.log_dir,
|
||||
sensor_history = state.sensors.history,
|
||||
)
|
||||
except RuntimeError as err:
|
||||
# Failed to export PNG, skip uploads below
|
||||
LOG.error('Failed to export graph: %s', err)
|
||||
test_object.report.append('Failed to export graph')
|
||||
return
|
||||
|
||||
# Upload PNG
|
||||
if not state.ost.disabled and state.ost.ticket_id:
|
||||
try:
|
||||
imgur_url = upload_to_imgur(image_path)
|
||||
nextcloud_url = upload_to_nextcloud(image_path, state.ost.ticket_id, 'cpu')
|
||||
test_object.report.append(f'Imgur: {imgur_url}')
|
||||
test_object.report.append(f'Nextcloud: {nextcloud_url}')
|
||||
except Exception as err:
|
||||
LOG.error('%s', err)
|
||||
LOG.error('Failed to upload graph')
|
||||
test_object.report.append('Failed to upload graph')
|
||||
|
||||
|
||||
def check_mprime_results(test_obj, working_dir) -> None:
|
||||
"""Check mprime log files and update test_obj."""
|
||||
|
|
|
|||
|
|
@ -5,19 +5,22 @@ import atexit
|
|||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import platform
|
||||
import subprocess
|
||||
|
||||
from docopt import docopt
|
||||
|
||||
from wk import cfg, debug, exe, log, std
|
||||
from wk.cfg.hw import STATUS_COLORS
|
||||
from wk import cfg, debug, exe, log, osticket, std
|
||||
from wk.cfg.hw import CPU_TEST_MINUTES, STATUS_COLORS
|
||||
from wk.hw import benchmark as hw_benchmark
|
||||
from wk.hw import cpu as hw_cpu
|
||||
from wk.hw import disk as hw_disk
|
||||
from wk.hw import osticket as hw_osticket
|
||||
from wk.hw import sensors as hw_sensors
|
||||
from wk.hw import smart as hw_smart
|
||||
from wk.hw import surface_scan as hw_surface_scan
|
||||
from wk.hw import system as hw_system
|
||||
from wk.hw import volumes as hw_volumes
|
||||
from wk.hw.audio import audio_test
|
||||
from wk.hw.keyboard import keyboard_test
|
||||
from wk.hw.network import network_test
|
||||
|
|
@ -35,15 +38,20 @@ Usage:
|
|||
hw-diags (-h | --help)
|
||||
|
||||
Options:
|
||||
-c --cli Force CLI mode
|
||||
-h --help Show this page
|
||||
-q --quick Skip menu and perform a quick check
|
||||
-c --cli Force CLI mode
|
||||
-h --help Show this page
|
||||
-q --quick Skip menu and perform a quick check
|
||||
-t --test-mode Run diags in test mode
|
||||
'''
|
||||
LOG = logging.getLogger(__name__)
|
||||
IO_SIZE_SKIP_NAME = (
|
||||
'Skip USB Benchmarks '
|
||||
f'(< {std.bytes_to_string(cfg.hw.IO_SMALL_DISK, use_binary=False)})'
|
||||
)
|
||||
TEST_GROUPS = {
|
||||
# Also used to build the menu options
|
||||
## NOTE: This needs to be above MENU_SETS
|
||||
'System Info': 'post_system_info',
|
||||
'CPU (Sysbench)': 'cpu_test_sysbench',
|
||||
'CPU (Prime95)': 'cpu_test_mprime',
|
||||
'CPU (Cooling)': 'cpu_test_cooling',
|
||||
|
|
@ -51,6 +59,7 @@ TEST_GROUPS = {
|
|||
'Disk Self-Test': 'disk_self_test',
|
||||
'Disk Surface Scan': 'disk_surface_scan',
|
||||
'Disk I/O Benchmark': 'disk_io_benchmark',
|
||||
'Disk Utilization': 'disk_volume_utilization',
|
||||
}
|
||||
MENU_ACTIONS = (
|
||||
'Audio Test',
|
||||
|
|
@ -62,7 +71,13 @@ MENU_ACTIONS = (
|
|||
MENU_ACTIONS_SECRET = (
|
||||
'Matrix',
|
||||
'Tubes',
|
||||
'?Secrets',
|
||||
)
|
||||
MENU_OPTIONS_SECRET = {
|
||||
'Ignore SMART errors': False,
|
||||
'Override CPU Testing Time (minutes)': CPU_TEST_MINUTES,
|
||||
'Override Surface Scan Error Limit': 1,
|
||||
}
|
||||
MENU_OPTIONS_QUICK = ('Disk Attributes',)
|
||||
MENU_SETS = {
|
||||
'Full Diagnostic': (*TEST_GROUPS,),
|
||||
|
|
@ -72,12 +87,16 @@ MENU_SETS = {
|
|||
'Disk Self-Test',
|
||||
'Disk Surface Scan',
|
||||
'Disk I/O Benchmark',
|
||||
'Disk Utilization',
|
||||
),
|
||||
'Disk Diagnostic (Quick)': ('Disk Attributes',),
|
||||
'Disk Diagnostic (Quick)': ('Disk Attributes', 'Disk Utilization'),
|
||||
}
|
||||
MENU_TOGGLES = (
|
||||
'Skip USB Benchmarks',
|
||||
'osTicket Integration',
|
||||
'osTicket Tech Note',
|
||||
IO_SIZE_SKIP_NAME,
|
||||
)
|
||||
NUM_DISK_TESTS = len([s for s in TEST_GROUPS if s.startswith('Disk')])
|
||||
PLATFORM = std.PLATFORM
|
||||
|
||||
# Classes
|
||||
|
|
@ -86,7 +105,9 @@ class State():
|
|||
def __init__(self, test_mode=False):
|
||||
self.disks: list[hw_disk.Disk] = []
|
||||
self.log_dir: pathlib.Path | None = None
|
||||
self.ost = osticket.osTicket()
|
||||
self.progress_file: pathlib.Path | None = None
|
||||
self.secret_menu = build_secret_menu()
|
||||
self.sensors: hw_sensors.Sensors = hw_sensors.Sensors()
|
||||
self.system: hw_system.System | None = None
|
||||
self.test_groups: list[TestGroup] = []
|
||||
|
|
@ -127,6 +148,11 @@ class State():
|
|||
self.disks.clear()
|
||||
self.sensors = hw_sensors.Sensors()
|
||||
self.test_groups.clear()
|
||||
self.ui.remove_all_subtitle_panes()
|
||||
|
||||
# osTicket
|
||||
self.ost.init()
|
||||
self.ost.disabled = not menu.toggles['osTicket Integration']['Selected']
|
||||
|
||||
# Set log
|
||||
self.log_dir = log.format_log_path(
|
||||
|
|
@ -161,6 +187,17 @@ class State():
|
|||
# Only add selected options
|
||||
continue
|
||||
|
||||
if 'System' in name:
|
||||
test = Test(dev=self.system, label=name, name=name)
|
||||
self.test_groups.insert(
|
||||
0,
|
||||
TestGroup(
|
||||
name=name,
|
||||
function=globals()[TEST_GROUPS[name]],
|
||||
test_objects=[test],
|
||||
)
|
||||
)
|
||||
|
||||
if 'CPU' in name:
|
||||
self.system.tests.append(
|
||||
Test(dev=self.system, label=name[5:-1], name=name),
|
||||
|
|
@ -178,8 +215,11 @@ class State():
|
|||
|
||||
# Group CPU tests
|
||||
if self.system.tests:
|
||||
index = 0
|
||||
if self.test_groups and self.test_groups[0].name.startswith('System'):
|
||||
index = 1
|
||||
self.test_groups.insert(
|
||||
0,
|
||||
index,
|
||||
TestGroup(
|
||||
name='CPU & Cooling',
|
||||
function=run_cpu_tests,
|
||||
|
|
@ -215,6 +255,16 @@ class State():
|
|||
for test in disk.tests:
|
||||
_f.write(f'\n{test.name}:\n')
|
||||
_f.write('\n'.join(debug.generate_object_report(test, indent=1)))
|
||||
if platform.system() == 'Darwin':
|
||||
cmd = [(
|
||||
f'sudo gpt -r show "{disk.path}"'
|
||||
f' >> {debug_dir}/gpt_{disk.path.name}.info'
|
||||
)]
|
||||
exe.run_program(cmd, check=False, shell=True)
|
||||
|
||||
# osTicket
|
||||
with open(f'{debug_dir}/osTicket.report', 'a', encoding='utf-8') as _f:
|
||||
_f.write('\n'.join(debug.generate_object_report(self.ost)))
|
||||
|
||||
# SMC
|
||||
if os.path.exists('/.wk-live-macos'):
|
||||
|
|
@ -281,6 +331,9 @@ def build_menu(cli_mode=False, quick_mode=False) -> cli.Menu:
|
|||
menu.add_set(name, {'Targets': targets})
|
||||
menu.actions['Start']['Separator'] = True
|
||||
|
||||
# osTicket
|
||||
menu.toggles['osTicket Tech Note']['Selected'] = False
|
||||
|
||||
# Update default selections for quick mode if necessary
|
||||
if quick_mode:
|
||||
for name, details in menu.options.items():
|
||||
|
|
@ -292,6 +345,7 @@ def build_menu(cli_mode=False, quick_mode=False) -> cli.Menu:
|
|||
menu.options['CPU (Sysbench)']['Selected'] = False
|
||||
menu.options['CPU (Prime95)']['Selected'] = False
|
||||
menu.options['CPU (Cooling)']['Selected'] = False
|
||||
menu.options['System Info']['Selected'] = False
|
||||
|
||||
# Add CLI actions if necessary
|
||||
if cli_mode or 'DISPLAY' not in os.environ:
|
||||
|
|
@ -317,6 +371,24 @@ def build_menu(cli_mode=False, quick_mode=False) -> cli.Menu:
|
|||
return menu
|
||||
|
||||
|
||||
def build_secret_menu() -> cli.Menu:
|
||||
title_text = [
|
||||
ansi.color_string(('Expert Settings', "(It's a secret to everyone!)"), ('ORANGE', None)),
|
||||
' ',
|
||||
ansi.color_string(
|
||||
[' !!', 'These settings can cause', 'MAJOR DAMAGE', 'to systems'],
|
||||
[None, 'YELLOW', 'RED', 'YELLOW'],
|
||||
),
|
||||
' !! Please read the manual before making changes',
|
||||
' ',
|
||||
]
|
||||
menu = cli.Menu(title='\n'.join(title_text))
|
||||
for option, value in MENU_OPTIONS_SECRET.items():
|
||||
menu.add_option(option, {'Selected': False, 'Value': value})
|
||||
menu.add_action('Main Menu')
|
||||
return menu
|
||||
|
||||
|
||||
def cpu_tests_init(state: State) -> None:
|
||||
"""Initialize CPU tests."""
|
||||
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
|
||||
|
|
@ -360,7 +432,7 @@ def cpu_test_cooling(state: State, test_object, test_mode=False) -> None:
|
|||
if test_object.disabled:
|
||||
return
|
||||
|
||||
hw_cpu.check_cooling_results(state.sensors, test_object)
|
||||
hw_cpu.check_cooling_results(state, test_object)
|
||||
state.update_progress_file()
|
||||
|
||||
|
||||
|
|
@ -373,6 +445,11 @@ def cpu_test_mprime(state: State, test_object, test_mode=False) -> None:
|
|||
test_minutes = cfg.hw.CPU_TEST_MINUTES
|
||||
if test_mode:
|
||||
test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT
|
||||
if state.secret_menu.options.get(
|
||||
'Override CPU Testing Time (minutes)')['Selected']:
|
||||
test_minutes = int(
|
||||
state.secret_menu.options.get('Override CPU Testing Time (minutes)')['Value'],
|
||||
)
|
||||
|
||||
# Bail early
|
||||
if test_object.disabled:
|
||||
|
|
@ -408,6 +485,7 @@ def cpu_test_mprime(state: State, test_object, test_mode=False) -> None:
|
|||
aborted = True
|
||||
|
||||
# Stop Prime95
|
||||
state.sensors.save_average_temps(temp_label='Cooldown', seconds=5)
|
||||
hw_cpu.stop_mprime(proc)
|
||||
|
||||
# Get cooldown temp
|
||||
|
|
@ -448,6 +526,11 @@ def cpu_test_sysbench(state: State, test_object, test_mode=False) -> None:
|
|||
test_minutes = cfg.hw.CPU_TEST_MINUTES
|
||||
if test_mode:
|
||||
test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT
|
||||
if state.secret_menu.options.get(
|
||||
'Override CPU Testing Time (minutes)')['Selected']:
|
||||
test_minutes = int(
|
||||
state.secret_menu.options.get('Override CPU Testing Time (minutes)')['Value'],
|
||||
)
|
||||
|
||||
# Bail early
|
||||
if test_object.disabled:
|
||||
|
|
@ -479,6 +562,7 @@ def cpu_test_sysbench(state: State, test_object, test_mode=False) -> None:
|
|||
LOG.error('Failed to find sysbench process', exc_info=True)
|
||||
except KeyboardInterrupt:
|
||||
aborted = True
|
||||
state.sensors.save_average_temps(temp_label='Cooldown', seconds=5)
|
||||
hw_cpu.stop_sysbench(proc, filehandle)
|
||||
|
||||
# Get cooldown temp
|
||||
|
|
@ -507,9 +591,11 @@ def cpu_test_sysbench(state: State, test_object, test_mode=False) -> None:
|
|||
# 0 == Completed w/out issue
|
||||
# -2 == Stopped with INT signal
|
||||
# -15 == Stopped with TERM signal
|
||||
test_object.failed = True
|
||||
test_object.set_status('Failed')
|
||||
test_object.report.append(f' Failed with return code: {proc.returncode}')
|
||||
else:
|
||||
test_object.passed = True
|
||||
test_object.set_status('Passed')
|
||||
test_object.report.append(' Completed without issue.')
|
||||
state.update_progress_file()
|
||||
|
|
@ -548,13 +634,18 @@ def disk_io_benchmark(
|
|||
)
|
||||
state.ui.set_current_pane_height(10)
|
||||
for test in test_objects:
|
||||
if test.disabled:
|
||||
# Skip
|
||||
if (
|
||||
skip_usb
|
||||
and test.dev.bus == 'USB'
|
||||
and test.dev.size < cfg.hw.IO_SMALL_DISK
|
||||
):
|
||||
test.set_status('Skipped')
|
||||
test.disabled = True
|
||||
continue
|
||||
|
||||
# Skip USB devices if requested
|
||||
if skip_usb and test.dev.bus == 'USB':
|
||||
test.set_status('Skipped')
|
||||
# Start benchmark
|
||||
for test in test_objects:
|
||||
if test.disabled:
|
||||
continue
|
||||
|
||||
# Start benchmark
|
||||
|
|
@ -571,7 +662,7 @@ def disk_io_benchmark(
|
|||
)
|
||||
state.update_progress_file()
|
||||
try:
|
||||
hw_benchmark.run_io_test(test, test_log, test_mode=test_mode)
|
||||
hw_benchmark.run_io_test(state, test, test_log, test_mode=test_mode)
|
||||
except KeyboardInterrupt:
|
||||
aborted = True
|
||||
except (subprocess.CalledProcessError, TypeError, ValueError) as err:
|
||||
|
|
@ -696,7 +787,12 @@ def disk_surface_scan(state: State, test_objects, test_mode=False) -> None:
|
|||
"""Read-only disk surface scan using badblocks."""
|
||||
LOG.info('Disk Surface Scan (badblocks)')
|
||||
aborted = False
|
||||
max_errors = 1
|
||||
threads = []
|
||||
if state.secret_menu.options.get(
|
||||
'Override Surface Scan Error Limit')['Selected']:
|
||||
max_errors = state.secret_menu.options.get(
|
||||
'Override Surface Scan Error Limit')['Value']
|
||||
|
||||
# Update panes
|
||||
state.update_title_text(
|
||||
|
|
@ -713,7 +809,7 @@ def disk_surface_scan(state: State, test_objects, test_mode=False) -> None:
|
|||
# Start thread
|
||||
test_log = f'{state.log_dir}/{test.dev.path.name}_badblocks.log'
|
||||
threads.append(exe.start_thread(
|
||||
hw_surface_scan.run_scan, args=(test, test_log, test_mode),
|
||||
hw_surface_scan.run_scan, args=(test, test_log, test_mode, max_errors),
|
||||
))
|
||||
|
||||
# Show progress
|
||||
|
|
@ -746,6 +842,17 @@ def disk_surface_scan(state: State, test_objects, test_mode=False) -> None:
|
|||
raise std.GenericAbort('Aborted')
|
||||
|
||||
|
||||
def disk_volume_utilization(state, test_objects, test_mode=False) -> None:
|
||||
"""Check disk for full volumes."""
|
||||
_ = test_mode
|
||||
LOG.info('Disk Utilization')
|
||||
for test in test_objects:
|
||||
hw_volumes.check_volume_utilization(test)
|
||||
|
||||
# Done
|
||||
state.update_progress_file()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main function for hardware diagnostics."""
|
||||
args = docopt(DOCSTRING)
|
||||
|
|
@ -762,6 +869,7 @@ def main() -> None:
|
|||
|
||||
# Quick Mode
|
||||
if args['--quick']:
|
||||
menu.toggles['osTicket Integration']['Selected'] = False
|
||||
run_diags(state, menu, quick_mode=True, test_mode=args['--test-mode'])
|
||||
return
|
||||
|
||||
|
|
@ -793,9 +901,11 @@ def main() -> None:
|
|||
state.ui.update_clock()
|
||||
|
||||
# Secrets
|
||||
if '?Secrets' in selection:
|
||||
state.secret_menu.settings_select()
|
||||
if 'Matrix' in selection:
|
||||
screensaver('matrix')
|
||||
elif 'Tubes' in selection:
|
||||
if 'Tubes' in selection:
|
||||
# Tubes ≈≈ Pipes?
|
||||
screensaver('pipes')
|
||||
|
||||
|
|
@ -817,6 +927,33 @@ def main() -> None:
|
|||
state.update_title_text('Main Menu')
|
||||
|
||||
|
||||
def post_system_info(state, test_objects, test_mode=False) -> None:
|
||||
"""Post system info to osTicket."""
|
||||
_ = test_mode
|
||||
|
||||
# Bail early
|
||||
if state.ost.disabled:
|
||||
return
|
||||
|
||||
# Build report
|
||||
report = state.system.generate_full_report()
|
||||
if state.disks:
|
||||
report.append('\n[Disks]')
|
||||
for disk in state.disks:
|
||||
report.append(f'... {disk.description}')
|
||||
|
||||
# Post to osTicket
|
||||
state.ost.post_response('\n'.join(report))
|
||||
|
||||
# Update test object(s)
|
||||
for test_object in test_objects:
|
||||
test_object.passed = True
|
||||
test_object.set_status('Done')
|
||||
|
||||
# Done
|
||||
state.update_progress_file()
|
||||
|
||||
|
||||
def print_countdown(proc, seconds) -> None:
|
||||
"""Print countdown to screen while proc is alive."""
|
||||
seconds = int(seconds)
|
||||
|
|
@ -854,8 +991,22 @@ def run_cpu_tests(state: State, test_objects, test_mode=False) -> None:
|
|||
cpu_tests_end(state)
|
||||
state.update_progress_file()
|
||||
|
||||
# Post results to osTicket
|
||||
if not state.ost.disabled:
|
||||
failed = any(test.failed for test in state.system.tests)
|
||||
cli.print_info('Posting results to osTicket...')
|
||||
state.ost.post_response(
|
||||
hw_osticket.build_report(state.system, 'CPU'),
|
||||
color='Diags FAIL' if failed else 'Diags',
|
||||
)
|
||||
|
||||
def run_diags(state: State, menu, quick_mode=False, test_mode=False) -> None:
|
||||
|
||||
def run_diags(
|
||||
state: State,
|
||||
menu: cli.Menu,
|
||||
quick_mode: bool = False,
|
||||
test_mode: bool = False,
|
||||
) -> None:
|
||||
"""Run selected diagnostics."""
|
||||
aborted = False
|
||||
atexit.register(state.save_debug_reports)
|
||||
|
|
@ -867,6 +1018,32 @@ def run_diags(state: State, menu, quick_mode=False, test_mode=False) -> None:
|
|||
cli.pause()
|
||||
return
|
||||
|
||||
# osTicket
|
||||
if not state.ost.disabled:
|
||||
# Select Ticket
|
||||
state.ost.select_ticket()
|
||||
|
||||
# Update top_text
|
||||
if state.ost.ticket_id:
|
||||
state.ui.add_subtitle_pane(
|
||||
cli.color_string(
|
||||
[f'#{state.ost.ticket_id}', str(state.ost.ticket_name)],
|
||||
[None, 'CYAN'],
|
||||
),
|
||||
str(state.ost.ticket_subject),
|
||||
)
|
||||
|
||||
# Add note
|
||||
if (state.ost.ticket_id
|
||||
and menu.toggles['osTicket Tech Note']['Selected']):
|
||||
note_lines = state.ost.add_note()
|
||||
if note_lines:
|
||||
state.ui.add_subtitle_pane(
|
||||
cli.color_string('Tech Note', 'YELLOW'),
|
||||
' | '.join(note_lines),
|
||||
)
|
||||
|
||||
|
||||
# Run tests
|
||||
for group in state.test_groups:
|
||||
|
||||
|
|
@ -874,7 +1051,7 @@ def run_diags(state: State, menu, quick_mode=False, test_mode=False) -> None:
|
|||
function = group.function
|
||||
args = [group.test_objects]
|
||||
if group.name == 'Disk I/O Benchmark':
|
||||
args.append(menu.toggles['Skip USB Benchmarks']['Selected'])
|
||||
args.append(menu.toggles[IO_SIZE_SKIP_NAME]['Selected'])
|
||||
state.ui.clear_current_pane()
|
||||
try:
|
||||
function(state, *args, test_mode=test_mode)
|
||||
|
|
@ -896,10 +1073,17 @@ def run_diags(state: State, menu, quick_mode=False, test_mode=False) -> None:
|
|||
if test.status == 'Pending':
|
||||
test.set_status('Aborted')
|
||||
|
||||
# Post disk results
|
||||
hw_osticket.post_disk_results(state, NUM_DISK_TESTS)
|
||||
|
||||
# Show results
|
||||
show_results(state)
|
||||
|
||||
# Update checkboxes
|
||||
hw_osticket.update_checkboxes(state, NUM_DISK_TESTS)
|
||||
|
||||
# Done
|
||||
state.ui.remove_all_subtitle_panes()
|
||||
state.save_debug_reports()
|
||||
atexit.unregister(state.save_debug_reports)
|
||||
if quick_mode:
|
||||
|
|
@ -930,7 +1114,7 @@ def show_results(state: State) -> None:
|
|||
]
|
||||
if cpu_tests_enabled:
|
||||
cli.print_success('CPU:')
|
||||
cli.print_report(state.system.generate_report())
|
||||
cli.print_report(state.system.generate_cpu_ram_report())
|
||||
cli.print_standard(' ')
|
||||
|
||||
# Disk Tests
|
||||
|
|
|
|||
216
scripts/wk/hw/osticket.py
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
"""WizardKit: osTicket hardware diagnostic functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from wk import osticket
|
||||
from wk.cfg.hw import (
|
||||
REGEX_BLOCK_GRAPH,
|
||||
REGEX_SMART_ATTRIBUTES,
|
||||
)
|
||||
from wk.hw import smart as hw_smart
|
||||
from wk.ui import cli
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Functions
|
||||
def build_report(dev, dev_type, num_disk_tests=None):
|
||||
"""Build report for posting to osTicket, returns str."""
|
||||
report = []
|
||||
|
||||
# Combined result
|
||||
if dev_type == 'CPU' or len(dev.tests) == num_disk_tests:
|
||||
# Build list of failed tests (if any)
|
||||
failed_tests = [t.name for t in dev.tests if t.failed]
|
||||
failed_tests = [name.replace('Disk ', '') for name in failed_tests]
|
||||
if len(failed_tests) > 2:
|
||||
failed_tests = f'{", ".join(failed_tests[:-1])}, & {failed_tests[-1]}'
|
||||
else:
|
||||
failed_tests = ' & '.join(failed_tests)
|
||||
|
||||
# Get overall result
|
||||
result = 'UNKNOWN'
|
||||
if any(t.failed for t in dev.tests):
|
||||
result = 'FAILED'
|
||||
elif all(t.passed for t in dev.tests):
|
||||
result = 'PASSED'
|
||||
|
||||
# Add to report
|
||||
report.append(
|
||||
f'{dev_type} hardware diagnostic tests: {result}'
|
||||
f'{" ("+failed_tests+")" if failed_tests else ""}'
|
||||
)
|
||||
report.append('')
|
||||
|
||||
# Description
|
||||
if hasattr(dev, 'cpu_description'):
|
||||
report.append(dev.cpu_description)
|
||||
else:
|
||||
report.append(dev.description)
|
||||
if hasattr(dev, 'ram_total'):
|
||||
if len(dev.ram_dimms) == 1 and 'justTotalRAM' in dev.ram_dimms[0]:
|
||||
report.append(f'{dev.ram_total} (Total - no DIMM info available)')
|
||||
else:
|
||||
report.append(f'{dev.ram_total} ({", ".join(dev.ram_dimms)})')
|
||||
if hasattr(dev, 'serial') and dev.serial:
|
||||
report.append(f'Serial Number: {dev.serial}')
|
||||
report.append('')
|
||||
|
||||
# Notes
|
||||
if hasattr(dev, 'notes') and dev.notes:
|
||||
report.append('Notes')
|
||||
report.extend([f'... {note}' for note in dev.notes])
|
||||
report.append('')
|
||||
|
||||
# Tests
|
||||
for test in dev.tests:
|
||||
report.append(f'{test.name} ({test.status})')
|
||||
|
||||
# Report
|
||||
if test.name == 'Disk Attributes' and dev.attributes:
|
||||
report.extend(
|
||||
convert_report(
|
||||
hw_smart.generate_attribute_report(dev),
|
||||
start_index=0,
|
||||
),
|
||||
)
|
||||
else:
|
||||
report.extend(convert_report(test.report, start_index=1))
|
||||
|
||||
# I/O graph upload report
|
||||
report.extend(getattr(test, 'upload_report', []))
|
||||
|
||||
# Spacer
|
||||
report.append('')
|
||||
|
||||
# Remove last line if empty
|
||||
if not report[-1].strip():
|
||||
report.pop(-1)
|
||||
|
||||
# Done
|
||||
return cli.strip_colors('\n'.join(report))
|
||||
|
||||
|
||||
def convert_report(original_report, start_index):
|
||||
"""Convert report to an osTicket compatible type, returns list."""
|
||||
report = []
|
||||
|
||||
# Convert report
|
||||
for line in original_report[start_index:]:
|
||||
# Remove colors and leading spaces
|
||||
line = cli.strip_colors(line)
|
||||
line = re.sub(r'^\s+', '', line)
|
||||
|
||||
# Disk I/O Benchmark
|
||||
if REGEX_BLOCK_GRAPH.search(line):
|
||||
line = REGEX_BLOCK_GRAPH.sub('', line)
|
||||
line = line.strip()
|
||||
|
||||
# SMART attributes
|
||||
match = REGEX_SMART_ATTRIBUTES.search(line)
|
||||
if match:
|
||||
# Switch decimal and hex labels
|
||||
_dec = f'{match.group("decimal"):>3}'
|
||||
_dec = osticket.pad_with_dots(_dec)
|
||||
_hex = match.group('hex')
|
||||
_data = match.group('data')
|
||||
line = f'{_hex}/{_dec}: {_data}'
|
||||
line = line.replace('failed', 'FAILED')
|
||||
|
||||
# Skip empty lines
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
# Fix inner spacing
|
||||
for spacing in re.findall(r'\s\s+', line):
|
||||
new_padding = osticket.pad_with_dots(spacing)
|
||||
new_padding += ' '
|
||||
line = line.replace(spacing, new_padding)
|
||||
|
||||
# Indent line
|
||||
line = f'... {line}'
|
||||
|
||||
# Add to (converted) report
|
||||
report.append(line)
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
||||
|
||||
def post_disk_results(state, num_disk_tests):
|
||||
"""Post disk test results for all disks."""
|
||||
disk_tests = []
|
||||
for group in state.test_groups:
|
||||
if group.name.startswith('Disk'):
|
||||
disk_tests.extend(group.test_objects)
|
||||
|
||||
# Bail if no disk tests were run
|
||||
if not disk_tests or state.ost.disabled:
|
||||
return
|
||||
|
||||
# Post disk results
|
||||
cli.print_info('Posting results to osTicket...')
|
||||
for disk in state.disks:
|
||||
state.ost.post_response(
|
||||
build_report(disk, 'Disk', num_disk_tests),
|
||||
color='Diags FAIL' if any(t.failed for t in disk.tests) else 'Diags',
|
||||
)
|
||||
|
||||
|
||||
def update_checkboxes(state, num_disk_tests):
|
||||
"""Update osTicket checkboxes after confirmation."""
|
||||
cpu_tests = []
|
||||
disk_tests = []
|
||||
num_disk_tests_run = len(state.test_groups)
|
||||
|
||||
# Build list of tests
|
||||
for group in state.test_groups:
|
||||
if group.name.startswith('CPU'):
|
||||
cpu_tests.extend(group.test_objects)
|
||||
num_disk_tests_run -= 1
|
||||
elif group.name.startswith('Disk'):
|
||||
disk_tests.extend(group.test_objects)
|
||||
elif group.name.startswith('System'):
|
||||
num_disk_tests_run -= 1
|
||||
|
||||
# Bail if osTicket integration disabled
|
||||
if state.ost.disabled:
|
||||
return
|
||||
|
||||
# Bail if values not confirmed
|
||||
if not cli.ask('Update osTicket checkboxes using the data above?'):
|
||||
return
|
||||
|
||||
# CPU max temp and pass/fail
|
||||
if cpu_tests:
|
||||
state.ost.set_cpu_max_temp(state.sensors.get_cpu_temp('Max'))
|
||||
if any(t.failed for t in cpu_tests):
|
||||
state.ost.set_flag_failed('CPU')
|
||||
elif all(t.passed for t in cpu_tests):
|
||||
state.ost.set_flag_passed('CPU')
|
||||
|
||||
# Check results for all disks
|
||||
if state.disks:
|
||||
all_disks_passed = True
|
||||
for disk in state.disks:
|
||||
if any(t.failed for t in disk.tests):
|
||||
# Mark failed disk in osTicket and stop checking results
|
||||
all_disks_passed = False
|
||||
state.ost.set_flag_failed('Disk')
|
||||
break
|
||||
if not all(t.passed for t in disk.tests):
|
||||
all_disks_passed = False
|
||||
break
|
||||
|
||||
# All disks passed
|
||||
if all_disks_passed and num_disk_tests_run == num_disk_tests:
|
||||
# Only mark as passed if a full disk diagnostic passed
|
||||
state.ost.set_flag_passed('Disk')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -10,7 +10,7 @@ from wk.cfg.hw import (
|
|||
BADBLOCKS_LARGE_DISK,
|
||||
BADBLOCKS_REGEX,
|
||||
BADBLOCKS_RESULTS_REGEX,
|
||||
BADBLOCKS_SKIP_REGEX,
|
||||
#BADBLOCKS_SKIP_REGEX,
|
||||
TEST_MODE_BADBLOCKS_LIMIT,
|
||||
)
|
||||
from wk.exe import run_program
|
||||
|
|
@ -25,34 +25,52 @@ LOG = logging.getLogger(__name__)
|
|||
# Functions
|
||||
def check_surface_scan_results(test_obj, log_path) -> None:
|
||||
"""Check results and set test status."""
|
||||
report = []
|
||||
report_color = None
|
||||
|
||||
# Read result
|
||||
with open(log_path, 'r', encoding='utf-8') as _f:
|
||||
for line in _f.readlines():
|
||||
line = ansi.strip_colors(line.strip())
|
||||
if not line or BADBLOCKS_SKIP_REGEX.match(line):
|
||||
if not line:
|
||||
# Skip
|
||||
continue
|
||||
|
||||
# Clean line by removing backspaces/etc
|
||||
line = BADBLOCKS_RESULTS_REGEX.sub(r'\1 \2', line)
|
||||
match = BADBLOCKS_RESULTS_REGEX.match(line)
|
||||
if match:
|
||||
if match.group(2) == ' done':
|
||||
line = BADBLOCKS_RESULTS_REGEX.sub(r'\1done', line)
|
||||
else:
|
||||
line = BADBLOCKS_RESULTS_REGEX.sub(r'\1\2', line)
|
||||
line = line.replace(r'\x08', '')
|
||||
line = line.strip()
|
||||
|
||||
# Add to report
|
||||
report.append(line)
|
||||
match = BADBLOCKS_REGEX.search(line)
|
||||
if match:
|
||||
if all(s == '0' for s in match.groups()):
|
||||
test_obj.passed = True
|
||||
test_obj.report.append(f' {line}')
|
||||
test_obj.set_status('Passed')
|
||||
else:
|
||||
test_obj.failed = True
|
||||
test_obj.report.append(f' {ansi.color_string(line, "YELLOW")}')
|
||||
test_obj.set_status('Failed')
|
||||
else:
|
||||
test_obj.report.append(f' {ansi.color_string(line, "YELLOW")}')
|
||||
|
||||
# Set report color and save to test_obj
|
||||
if test_obj.failed:
|
||||
report_color = 'RED'
|
||||
elif not test_obj.passed:
|
||||
report_color = 'YELLOW'
|
||||
for line in report:
|
||||
test_obj.report.append(f' {ansi.color_string(line, report_color)}')
|
||||
|
||||
# Handle undefined result status
|
||||
if not (test_obj.passed or test_obj.failed):
|
||||
test_obj.set_status('Unknown')
|
||||
|
||||
|
||||
def run_scan(test_obj, log_path, test_mode=False) -> None:
|
||||
def run_scan(test_obj, log_path, test_mode=False, max_errors=1) -> None:
|
||||
"""Run surface scan and handle exceptions."""
|
||||
block_size = '1024'
|
||||
dev = test_obj.dev
|
||||
|
|
@ -71,8 +89,17 @@ def run_scan(test_obj, log_path, test_mode=False) -> None:
|
|||
or dev.size >= BADBLOCKS_LARGE_DISK):
|
||||
block_size = '4096'
|
||||
|
||||
# Max errors
|
||||
if int(max_errors) <= 0:
|
||||
max_errors = ''
|
||||
else:
|
||||
max_errors = f'-e{max_errors}'
|
||||
|
||||
# Start scan
|
||||
cmd = ['sudo', 'badblocks', '-sv', '-b', block_size, '-e', '1', dev_path]
|
||||
cmd = ['sudo', 'badblocks', '-sv', '-b', block_size]
|
||||
if max_errors:
|
||||
cmd.append(max_errors)
|
||||
cmd.append(dev_path)
|
||||
if test_mode:
|
||||
# Only test a limited scope instead of the whole device
|
||||
cmd.append(TEST_MODE_BADBLOCKS_LIMIT)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
import os
|
||||
import plistlib
|
||||
import re
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ from wk.ui import ansi
|
|||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
EVERYMAC_URL = 'https://everymac.com/ultimate-mac-lookup/?search_keywords='
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
|
|
@ -33,7 +35,7 @@ class System:
|
|||
self.set_cpu_description()
|
||||
self.get_ram_details()
|
||||
|
||||
def generate_report(self) -> list[str]:
|
||||
def generate_cpu_ram_report(self) -> list[str]:
|
||||
"""Generate CPU & RAM report, returns list."""
|
||||
report = []
|
||||
report.append(ansi.color_string('Device', 'BLUE'))
|
||||
|
|
@ -49,6 +51,38 @@ class System:
|
|||
|
||||
return report
|
||||
|
||||
def generate_full_report(self) -> list[str]:
|
||||
"""Generate full report, returns list."""
|
||||
report = ['[System]']
|
||||
report.extend([f'... {line}' for line in self.get_system_info()])
|
||||
report.append('\n[Motherboard]')
|
||||
report.extend([f'... {line}' for line in self.get_mobo_info()])
|
||||
report.append('\n[BIOS]')
|
||||
report.extend([f'... {line}' for line in self.get_bios_info()])
|
||||
report.append('\n[CPU]')
|
||||
report.append(f'... {self.cpu_description}')
|
||||
report.append('\n[RAM]')
|
||||
report.append(f'... {self.ram_total} ({", ".join(self.ram_dimms)})')
|
||||
report.append('\n[GPU]')
|
||||
report.extend([f'... {line}' for line in self.get_gpu_info()])
|
||||
return report
|
||||
|
||||
def get_bios_info(self) -> list[str]:
|
||||
"""Get BIOS details, returns list."""
|
||||
report = []
|
||||
|
||||
# Bail early
|
||||
if PLATFORM != 'Linux':
|
||||
# Only Linux is supported ATM
|
||||
return report
|
||||
|
||||
# Get details
|
||||
report.append(f'Version: {get_dmi_info_linux("bios_version")}')
|
||||
report.append(f'Released: {get_dmi_info_linux("bios_date")}')
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
||||
def get_cpu_details(self) -> None:
|
||||
"""Get CPU details using OS specific methods."""
|
||||
cmd = ['lscpu', '--json']
|
||||
|
|
@ -68,6 +102,78 @@ class System:
|
|||
continue
|
||||
self.raw_details[_field] = _data
|
||||
|
||||
def get_gpu_info(self) -> list[str]:
|
||||
"""Get GPU details, returns list."""
|
||||
report = []
|
||||
|
||||
# Bail early
|
||||
if PLATFORM != 'Linux':
|
||||
# Only Linux is supported ATM
|
||||
return report
|
||||
|
||||
# Get PCI details
|
||||
proc = run_program(['lspci'])
|
||||
for line in proc.stdout.splitlines():
|
||||
if 'VGA' not in line:
|
||||
continue
|
||||
line = re.sub('^.*:', '', line)
|
||||
line = re.sub('Integrated Graphics Controller.*', 'iGPU', line)
|
||||
line = line.replace('Advanced Micro Devices, Inc.', 'AMD')
|
||||
line = line.replace('Intel Corporation', 'Intel')
|
||||
line = line.replace('Generation Core Processor Family', 'Gen')
|
||||
report.append(f'{line.strip()}')
|
||||
|
||||
# Get GLX info
|
||||
if 'DISPLAY' in os.environ or 'WAYLAND_DISPLAY' in os.environ:
|
||||
proc = run_program(['glxinfo'])
|
||||
for line in proc.stdout.splitlines():
|
||||
if 'OpenGL renderer' in line:
|
||||
line = re.sub('^.*:', '', line)
|
||||
report.append(line.strip())
|
||||
break
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
||||
def get_mobo_info(self) -> list[str]:
|
||||
"""Get motherboard details, returns list."""
|
||||
report = []
|
||||
|
||||
# Bail early
|
||||
if PLATFORM != 'Linux':
|
||||
# Only Linux is supported ATM
|
||||
return report
|
||||
|
||||
# Get details
|
||||
report.append(f'Vendor: {get_dmi_info_linux("board_vendor")}')
|
||||
report.append(f'Name: {get_dmi_info_linux("board_name")}')
|
||||
report.append(f'Version: {get_dmi_info_linux("board_version")}')
|
||||
report.append(f'Serial: {get_dmi_info_linux("board_serial")}')
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
||||
def get_system_info(self) -> list[str]:
|
||||
"""Get system details, returns list."""
|
||||
report = []
|
||||
|
||||
# Bail early
|
||||
if PLATFORM != 'Linux':
|
||||
# Only Linux is supported ATM
|
||||
return report
|
||||
|
||||
# Get details
|
||||
vendor = get_dmi_info_linux("sys_vendor")
|
||||
serial = get_dmi_info_linux("product_serial")
|
||||
report.append(f'Vendor: {vendor}')
|
||||
report.append(f'Name: {get_dmi_info_linux("product_name")}')
|
||||
report.append(f'Serial: {serial}')
|
||||
if 'apple' in vendor.lower():
|
||||
report.append(f'{EVERYMAC_URL}{serial}')
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
||||
def get_ram_details(self) -> None:
|
||||
"""Get RAM details using OS specific methods."""
|
||||
if PLATFORM == 'Darwin':
|
||||
|
|
@ -105,6 +211,16 @@ class System:
|
|||
self.cpu_description = re.sub(r'\s+', ' ', proc.stdout.strip())
|
||||
|
||||
|
||||
def get_dmi_info_linux(value) -> str:
|
||||
"""Get DMI info, returns str."""
|
||||
dmi_path = '/sys/devices/virtual/dmi/id'
|
||||
cmd = ['sudo', 'cat', f'{dmi_path}/{value}']
|
||||
proc = run_program(cmd, check=False)
|
||||
if proc.returncode:
|
||||
return '[???]'
|
||||
return proc.stdout.strip()
|
||||
|
||||
|
||||
def get_ram_list_linux() -> list[list]:
|
||||
"""Get RAM list using dmidecode."""
|
||||
cmd = ['sudo', 'dmidecode', '--type', 'memory']
|
||||
|
|
|
|||
123
scripts/wk/hw/volumes.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
"""WizardKit: Volume functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from wk import os as wk_os
|
||||
from wk.cfg.hw import (
|
||||
VOLUME_FAILURE_THRESHOLD,
|
||||
VOLUME_WARNING_THRESHOLD,
|
||||
VOLUME_SIZE_THRESHOLD,
|
||||
)
|
||||
from wk.std import PLATFORM, bytes_to_string
|
||||
from wk.ui.ansi import color_string
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Functions
|
||||
def add_dev_line(test_obj, details) -> None:
|
||||
"""Add device line to test report."""
|
||||
if details is test_obj.dev:
|
||||
details = details.raw_details
|
||||
filesystem = details['fstype']
|
||||
report_line = re.sub(r"^/dev/", " ", details['name'])
|
||||
label = details['label']
|
||||
if label:
|
||||
label = f', "{color_string(label, "CYAN")}"'
|
||||
report_line += f' ({filesystem}{label if label else ""})'
|
||||
|
||||
# Bail early
|
||||
if not filesystem:
|
||||
# Skip devices without a filesystem
|
||||
return
|
||||
|
||||
# Get sizes
|
||||
used = -1
|
||||
percent_used = -1
|
||||
size = details['size']
|
||||
if PLATFORM == 'Darwin':
|
||||
size = int(details.get('TotalSize', -1))
|
||||
free = int(details.get('FreeSpace', 0))
|
||||
used = size - free
|
||||
elif PLATFORM == 'Linux':
|
||||
free = details.get('fsavail', 0)
|
||||
used = details.get('fsused', -1)
|
||||
if free is None:
|
||||
free = 0
|
||||
if used is None:
|
||||
used = -1
|
||||
percent_used = (used / size) * 100
|
||||
|
||||
# Report Bitlocker
|
||||
if filesystem == 'BitLocker':
|
||||
test_obj.report.append(f'{report_line} {bytes_to_string(size)}')
|
||||
|
||||
# Handle unsupported devices
|
||||
if not details['mountpoint']:
|
||||
# Under Linux the volume needs to be mounted to get used space
|
||||
used = -1
|
||||
|
||||
# Check for failures
|
||||
if (used > 0
|
||||
and percent_used >= VOLUME_FAILURE_THRESHOLD
|
||||
and size >= VOLUME_SIZE_THRESHOLD * 1024**3):
|
||||
test_obj.failed = True
|
||||
|
||||
# Build and color size_line if needed
|
||||
color = None
|
||||
if test_obj.failed:
|
||||
color = 'RED'
|
||||
elif percent_used >= VOLUME_WARNING_THRESHOLD:
|
||||
color = 'YELLOW'
|
||||
size_line = f'{bytes_to_string(size)}'
|
||||
if used > 0:
|
||||
size_line += f' ({bytes_to_string(used)} used, {percent_used:0.0f}% full)'
|
||||
size_line = color_string(size_line, str(color))
|
||||
|
||||
# Done
|
||||
test_obj.report.append(f'{report_line} {size_line}')
|
||||
|
||||
|
||||
def check_volume_utilization(test_obj) -> None:
|
||||
"""Check volume utilization using OS specific methods."""
|
||||
dev = test_obj.dev
|
||||
|
||||
# Mount all volumes (read only if possible)
|
||||
mount_all_volumes(test_obj.dev)
|
||||
dev.update_details(skip_children=False)
|
||||
|
||||
# Build report
|
||||
test_obj.report.append(color_string('Disk Utilization', 'BLUE'))
|
||||
for _d in (dev, *dev.children):
|
||||
add_dev_line(test_obj, _d)
|
||||
|
||||
# Update test object
|
||||
if test_obj.failed:
|
||||
test_obj.dev.add_note('Full volume(s) detected', color='YELLOW')
|
||||
test_obj.passed = False
|
||||
test_obj.set_status('Failed')
|
||||
else:
|
||||
test_obj.passed = True
|
||||
test_obj.set_status('Passed')
|
||||
|
||||
|
||||
def mount_all_volumes(dev) -> None:
|
||||
"""Mount all volumes for dev using."""
|
||||
if PLATFORM == 'Darwin':
|
||||
# NOTE: Disabled due to a bug they should already be mounted by macOS
|
||||
#wk_os.mac.mount_disk(device_path=dev.path)
|
||||
pass
|
||||
elif PLATFORM == 'Linux':
|
||||
wk_os.linux.mount_volumes(
|
||||
device_path=dev.path,
|
||||
read_write=False,
|
||||
scan_corestorage=not any(t.failed for t in dev.tests),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -11,7 +11,6 @@ import re
|
|||
|
||||
from wk.cfg.launchers import LAUNCHERS
|
||||
from wk.cfg.main import ARCHIVE_PASSWORD, KIT_NAME_FULL
|
||||
from wk.cfg.music import MUSIC_MOD, MUSIC_SNES, MUSIC_SNES_BAD
|
||||
from wk.cfg.sources import SOURCES
|
||||
from wk.exe import popen_program, run_program, wait_for_procs
|
||||
from wk.io import copy_file, delete_item, recursive_copy, rename_item
|
||||
|
|
@ -123,9 +122,16 @@ def download_aida64() -> None:
|
|||
|
||||
def download_autoruns() -> None:
|
||||
"""Download Autoruns."""
|
||||
for item in ('Autoruns32', 'Autoruns64'):
|
||||
out_path = BIN_DIR.joinpath(f'Sysinternals/{item}.exe')
|
||||
download_file(out_path, SOURCES[item])
|
||||
out_path = BIN_DIR.joinpath(f'Sysinternals/Autoruns64.exe')
|
||||
download_file(out_path, SOURCES['Autoruns64'])
|
||||
|
||||
|
||||
def download_bcuninstaller() -> None:
|
||||
"""Download Bulk Crap Uninstaller."""
|
||||
archive = download_to_temp('BCU.zip', SOURCES['BCUninstaller'])
|
||||
extract_to_bin(archive, 'BCUninstaller')
|
||||
delete_from_temp('BCU.zip')
|
||||
delete_item(BIN_DIR.joinpath('BCUninstaller/win-x86'))
|
||||
|
||||
|
||||
def download_bleachbit() -> None:
|
||||
|
|
@ -145,9 +151,6 @@ def download_bleachbit() -> None:
|
|||
|
||||
def download_bluescreenview() -> None:
|
||||
"""Download BlueScreenView."""
|
||||
archive_32 = download_to_temp(
|
||||
'bluescreenview32.zip', SOURCES['BlueScreenView32'],
|
||||
)
|
||||
archive_64 = download_to_temp(
|
||||
'bluescreenview64.zip', SOURCES['BlueScreenView64'],
|
||||
)
|
||||
|
|
@ -157,11 +160,21 @@ def download_bluescreenview() -> None:
|
|||
out_path.joinpath('BlueScreenView.exe'),
|
||||
out_path.joinpath('BlueScreenView64.exe'),
|
||||
)
|
||||
extract_archive(archive_32, out_path)
|
||||
delete_from_temp('bluescreenview32.zip')
|
||||
delete_from_temp('bluescreenview64.zip')
|
||||
|
||||
|
||||
def download_coretemp():
|
||||
"""Download Core Temp."""
|
||||
archive_64 = download_to_temp('coretemp64.zip', SOURCES['CoreTemp64'])
|
||||
out_path = BIN_DIR.joinpath('CoreTemp')
|
||||
extract_archive(archive_64, out_path, 'Core Temp.exe')
|
||||
rename_item(
|
||||
out_path.joinpath('Core Temp.exe'),
|
||||
out_path.joinpath('CoreTemp64.exe'),
|
||||
)
|
||||
delete_from_temp('coretemp64.zip')
|
||||
|
||||
|
||||
def download_ddu() -> None:
|
||||
"""Download Display Driver Uninstaller."""
|
||||
archive = download_to_temp('DDU.exe', SOURCES['DDU'])
|
||||
|
|
@ -178,13 +191,6 @@ def download_ddu() -> None:
|
|||
delete_from_temp('DDU.exe')
|
||||
|
||||
|
||||
def download_bcuninstaller() -> None:
|
||||
"""Download Bulk Crap Uninstaller."""
|
||||
archive = download_to_temp('BCU.zip', SOURCES['BCUninstaller'])
|
||||
extract_to_bin(archive, 'BCUninstaller')
|
||||
delete_from_temp('BCU.zip')
|
||||
|
||||
|
||||
def download_erunt() -> None:
|
||||
"""Download ERUNT."""
|
||||
archive = download_to_temp('erunt.zip', SOURCES['ERUNT'])
|
||||
|
|
@ -194,7 +200,6 @@ def download_erunt() -> None:
|
|||
|
||||
def download_everything() -> None:
|
||||
"""Download Everything."""
|
||||
archive_32 = download_to_temp('everything32.zip', SOURCES['Everything32'])
|
||||
archive_64 = download_to_temp('everything64.zip', SOURCES['Everything64'])
|
||||
out_path = BIN_DIR.joinpath('Everything')
|
||||
extract_archive(archive_64, out_path, 'Everything.exe')
|
||||
|
|
@ -202,8 +207,6 @@ def download_everything() -> None:
|
|||
out_path.joinpath('Everything.exe'),
|
||||
out_path.joinpath('Everything64.exe'),
|
||||
)
|
||||
extract_archive(archive_32, out_path)
|
||||
delete_from_temp('everything32.zip')
|
||||
delete_from_temp('everything64.zip')
|
||||
|
||||
|
||||
|
|
@ -211,14 +214,11 @@ def download_fastcopy() -> None:
|
|||
"""Download FastCopy."""
|
||||
installer = download_to_temp('FastCopyInstaller.exe', SOURCES['FastCopy'])
|
||||
out_path = BIN_DIR.joinpath('FastCopy')
|
||||
tmp_path = TMP_DIR.joinpath('FastCopy64')
|
||||
run_program([installer, '/NOSUBDIR', f'/DIR={out_path}', '/EXTRACT32'])
|
||||
run_program([installer, '/NOSUBDIR', f'/DIR={tmp_path}', '/EXTRACT64'])
|
||||
run_program([installer, '/NOSUBDIR', f'/DIR={out_path}', '/EXTRACT64'])
|
||||
rename_item(
|
||||
tmp_path.joinpath('FastCopy.exe'),
|
||||
out_path.joinpath('FastCopy.exe'),
|
||||
out_path.joinpath('FastCopy64.exe'),
|
||||
)
|
||||
delete_from_temp('FastCopy64')
|
||||
delete_from_temp('FastCopyInstaller.exe')
|
||||
delete_item(BIN_DIR.joinpath('FastCopy/setup.exe'))
|
||||
|
||||
|
|
@ -243,11 +243,23 @@ def download_furmark() -> None:
|
|||
delete_from_temp('FurMarkInstall')
|
||||
|
||||
|
||||
def download_hwinfo() -> None:
|
||||
"""Download HWiNFO."""
|
||||
archive = download_to_temp('HWiNFO.zip', SOURCES['HWiNFO'])
|
||||
extract_to_bin(archive, 'HWiNFO')
|
||||
delete_from_temp('HWiNFO.zip')
|
||||
def download_libreoffice() -> None:
|
||||
"""Download LibreOffice."""
|
||||
out_path = INSTALLERS_DIR.joinpath(f'LibreOffice64.msi')
|
||||
download_file(
|
||||
out_path,
|
||||
SOURCES[f'LibreOffice64'],
|
||||
referer='https://www.libreoffice.org/download/download-libreoffice/',
|
||||
)
|
||||
ui.sleep(1)
|
||||
|
||||
|
||||
def download_linux_reader():
|
||||
"""Download Linux Reader."""
|
||||
installer = download_to_temp('LinuxReader.exe', SOURCES['Linux Reader'])
|
||||
out_path = BIN_DIR.joinpath('LinuxReader')
|
||||
extract_archive(installer, out_path, '-x!*PLUGINSDIR', '-x!Uninstall*')
|
||||
delete_from_temp('LinuxReader.exe')
|
||||
|
||||
|
||||
def download_macs_fan_control() -> None:
|
||||
|
|
@ -256,18 +268,6 @@ def download_macs_fan_control() -> None:
|
|||
download_file(out_path, SOURCES['Macs Fan Control'])
|
||||
|
||||
|
||||
def download_libreoffice() -> None:
|
||||
"""Download LibreOffice."""
|
||||
for arch in 32, 64:
|
||||
out_path = INSTALLERS_DIR.joinpath(f'LibreOffice{arch}.msi')
|
||||
download_file(
|
||||
out_path,
|
||||
SOURCES[f'LibreOffice{arch}'],
|
||||
referer='https://www.libreoffice.org/download/download-libreoffice/',
|
||||
)
|
||||
ui.sleep(1)
|
||||
|
||||
|
||||
def download_neutron() -> None:
|
||||
"""Download Neutron."""
|
||||
archive = download_to_temp('neutron.zip', SOURCES['Neutron'])
|
||||
|
|
@ -295,6 +295,13 @@ def download_openshell() -> None:
|
|||
download_file(out_path, SOURCES[name[:-4]])
|
||||
|
||||
|
||||
def download_prime95():
|
||||
"""Download Prime95."""
|
||||
archive = download_to_temp('prime95.zip', SOURCES['Prime95'])
|
||||
extract_to_bin(archive, 'Prime95')
|
||||
delete_from_temp('prime95.zip')
|
||||
|
||||
|
||||
def download_putty() -> None:
|
||||
"""Download PuTTY."""
|
||||
archive = download_to_temp('putty.zip', SOURCES['PuTTY'])
|
||||
|
|
@ -302,6 +309,12 @@ def download_putty() -> None:
|
|||
delete_from_temp('putty.zip')
|
||||
|
||||
|
||||
def download_shutup10():
|
||||
"""Download O&O ShutUp10."""
|
||||
out_path = BIN_DIR.joinpath('ShutUp10/OOSU10.exe')
|
||||
download_file(out_path, SOURCES['ShutUp10'])
|
||||
|
||||
|
||||
def download_snappy_driver_installer_origin() -> None:
|
||||
"""Download Snappy Driver Installer Origin."""
|
||||
archive = download_to_temp('aria2.zip', SOURCES['Aria2'])
|
||||
|
|
@ -372,6 +385,29 @@ def download_snappy_driver_installer_origin() -> None:
|
|||
delete_from_temp('fake.7z')
|
||||
|
||||
|
||||
def download_windows_repair_aio():
|
||||
"""Download Windows Repair AIO."""
|
||||
archive = download_to_temp('winrepairaio.zip', SOURCES['Windows Repair AIO'])
|
||||
out_path = BIN_DIR.joinpath('WinRepairAIO')
|
||||
tmp_path = TMP_DIR.joinpath('WinRepairAIO')
|
||||
extract_archive(archive, tmp_path)
|
||||
for item in tmp_path.joinpath('Tweaking.com - Windows Repair').iterdir():
|
||||
try:
|
||||
rename_item(item, out_path.joinpath(item.name))
|
||||
except FileExistsError:
|
||||
# Ignore and use our defaults
|
||||
pass
|
||||
delete_from_temp('WinRepairAIO')
|
||||
delete_from_temp('winrepairaio.zip')
|
||||
|
||||
|
||||
def download_winscp():
|
||||
"""Download WinSCP."""
|
||||
archive = download_to_temp('winscp.zip', SOURCES['WinSCP'])
|
||||
extract_to_bin(archive, 'WinSCP')
|
||||
delete_from_temp('winscp.zip')
|
||||
|
||||
|
||||
def download_wiztree() -> None:
|
||||
"""Download WizTree."""
|
||||
archive = download_to_temp('wiztree.zip', SOURCES['WizTree'])
|
||||
|
|
@ -407,57 +443,6 @@ def download_xmplay() -> None:
|
|||
delete_from_temp('xmp-rar.zip')
|
||||
delete_from_temp('Innocuous.zip')
|
||||
|
||||
def download_xmplay_music() -> None:
|
||||
"""Download XMPlay Music."""
|
||||
music_tmp = TMP_DIR.joinpath('music')
|
||||
music_tmp.mkdir(exist_ok=True)
|
||||
current_dir = os.getcwd()
|
||||
os.chdir(music_tmp)
|
||||
url_mod = 'https://api.modarchive.org/downloads.php'
|
||||
url_rsn = 'http://snesmusic.org/v2/download.php'
|
||||
|
||||
# Download music
|
||||
for song_id, song_name in MUSIC_MOD:
|
||||
download_file(
|
||||
music_tmp.joinpath(f'MOD/{song_name}'),
|
||||
f'{url_mod}?moduleid={song_id}#{song_name}',
|
||||
)
|
||||
for game in MUSIC_SNES:
|
||||
download_file(
|
||||
music_tmp.joinpath(f'SNES/{game}.rsn'),
|
||||
f'{url_rsn}?spcNow={game}',
|
||||
)
|
||||
|
||||
# Extract SNES archives
|
||||
for item in music_tmp.joinpath('SNES').iterdir():
|
||||
cmd = [
|
||||
SEVEN_ZIP,
|
||||
'x', item, f'-oSNES\\{item.stem}',
|
||||
'-bso0', '-bse0', '-bsp0',
|
||||
]
|
||||
run_program(cmd)
|
||||
delete_item(item)
|
||||
|
||||
# Remove bad songs
|
||||
for game, globs in MUSIC_SNES_BAD.items():
|
||||
for glob in globs:
|
||||
for item in music_tmp.joinpath(f'SNES/{game}').glob(glob):
|
||||
delete_item(item)
|
||||
|
||||
# Compress music
|
||||
cmd = [
|
||||
SEVEN_ZIP,
|
||||
'a', '-t7z', '-mx=9',
|
||||
'-bso0', '-bse0', '-bsp0',
|
||||
BIN_DIR.joinpath('XMPlay/music.7z'),
|
||||
'MOD', 'SNES',
|
||||
]
|
||||
run_program(cmd)
|
||||
os.chdir(current_dir)
|
||||
|
||||
# Cleanup
|
||||
delete_from_temp('music')
|
||||
|
||||
|
||||
# "Main" Function
|
||||
def build_kit() -> None:
|
||||
|
|
@ -482,23 +467,27 @@ def build_kit() -> None:
|
|||
try_print.run('Autoruns...', download_autoruns)
|
||||
try_print.run('BleachBit...', download_bleachbit)
|
||||
try_print.run('BlueScreenView...', download_bluescreenview)
|
||||
try_print.run('CoreTemp...', download_coretemp)
|
||||
try_print.run('ERUNT...', download_erunt)
|
||||
try_print.run('BulkCrapUninstaller...', download_bcuninstaller)
|
||||
try_print.run('DDU...', download_ddu)
|
||||
try_print.run('Everything...', download_everything)
|
||||
try_print.run('FastCopy...', download_fastcopy)
|
||||
try_print.run('FurMark...', download_furmark)
|
||||
try_print.run('HWiNFO...', download_hwinfo)
|
||||
try_print.run('LibreOffice...', download_libreoffice)
|
||||
try_print.run('Linux Reader...', download_linux_reader)
|
||||
try_print.run('Macs Fan Control...', download_macs_fan_control)
|
||||
try_print.run('Neutron...', download_neutron)
|
||||
try_print.run('Notepad++...', download_notepad_plus_plus)
|
||||
try_print.run('O&O ShutUp10...', download_shutup10)
|
||||
try_print.run('OpenShell...', download_openshell)
|
||||
try_print.run('Prime95...', download_prime95)
|
||||
try_print.run('PuTTY...', download_putty)
|
||||
try_print.run('Snappy Driver Installer...', download_snappy_driver_installer_origin)
|
||||
try_print.run('WinSCP...', download_winscp)
|
||||
try_print.run('Windows Repair AIO...', download_windows_repair_aio)
|
||||
try_print.run('WizTree...', download_wiztree)
|
||||
try_print.run('XMPlay...', download_xmplay)
|
||||
try_print.run('XMPlay Music...', download_xmplay_music)
|
||||
try_print.run('Snappy Driver Installer...', download_snappy_driver_installer_origin)
|
||||
|
||||
# Pause
|
||||
print('', flush=True)
|
||||
|
|
|
|||
|
|
@ -6,14 +6,18 @@ import pathlib
|
|||
import platform
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from subprocess import CompletedProcess, Popen
|
||||
from subprocess import CalledProcessError, CompletedProcess, Popen
|
||||
|
||||
import requests
|
||||
|
||||
from wk.cfg.main import ARCHIVE_PASSWORD
|
||||
from wk.cfg.net import SDIO_SERVER
|
||||
from wk.cfg.sources import DOWNLOAD_FREQUENCY, SOURCES
|
||||
from wk.exe import popen_program, run_program
|
||||
from wk.io import get_path_obj
|
||||
from wk.net import mount_network_share
|
||||
from wk.std import GenericError, sleep
|
||||
from wk.ui.cli import abort, ask, print_error
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
|
|
@ -25,6 +29,14 @@ HEADERS = {
|
|||
'Gecko/20100101 Firefox/97.0'
|
||||
),
|
||||
}
|
||||
MOUNT_EXCEPTIONS = (RuntimeError, CalledProcessError)
|
||||
SDIO_REMOTE_PATH = get_path_obj(
|
||||
(
|
||||
fr'\\{SDIO_SERVER["Address"]}\{SDIO_SERVER["Share"]}\{SDIO_SERVER["Path"]}'
|
||||
fr'\SDIO{"64" if ARCH == "64" else ""}.exe'
|
||||
),
|
||||
resolve=False,
|
||||
)
|
||||
|
||||
|
||||
# "GLOBAL" VARIABLES
|
||||
|
|
@ -203,6 +215,52 @@ def get_tool_path(folder, name, check=True, suffix=None) -> pathlib.Path:
|
|||
return tool_path
|
||||
|
||||
|
||||
def get_sdio_path(interactive: bool) -> pathlib.Path:
|
||||
"""Try to mount SDIO server."""
|
||||
use_network = False
|
||||
sdio_path = get_tool_path("SDIO", "SDIO")
|
||||
def _mount_server() -> CompletedProcess:
|
||||
if interactive:
|
||||
print('Connecting to server... (Press CTRL+c to use local copy)')
|
||||
return mount_network_share(SDIO_SERVER, read_write=False)
|
||||
def _try_again(error_msg: str) -> bool:
|
||||
"""Ask to try again or quit."""
|
||||
print_error(error_msg)
|
||||
if ask(' Try again?'):
|
||||
return True
|
||||
if not ask(' Use local version?'):
|
||||
abort()
|
||||
return False
|
||||
|
||||
# Bail early
|
||||
if not SDIO_SERVER['Address']:
|
||||
return sdio_path
|
||||
|
||||
# Main loop
|
||||
while True:
|
||||
try:
|
||||
proc = _mount_server()
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except MOUNT_EXCEPTIONS as err:
|
||||
if not (interactive and _try_again(f' ERROR: {err}')):
|
||||
break
|
||||
else:
|
||||
if proc.returncode == 0:
|
||||
# Network copy available
|
||||
use_network = True
|
||||
break
|
||||
|
||||
# Failed to mount
|
||||
if not (interactive and _try_again(' Failed to mount server')):
|
||||
break
|
||||
|
||||
# Done
|
||||
if use_network:
|
||||
sdio_path = SDIO_REMOTE_PATH
|
||||
return sdio_path
|
||||
|
||||
|
||||
def run_tool(
|
||||
folder, name, *run_args,
|
||||
cbin=False, cwd=False, download=False, popen=False,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ Usage:
|
|||
[--linux PATH]
|
||||
[--main-kit PATH]
|
||||
[--winpe PATH]
|
||||
[--eset PATH]
|
||||
[--extra-dir PATH]
|
||||
[EXTRA_IMAGES...]
|
||||
build-ufd (-h | --help)
|
||||
|
|
@ -44,6 +45,7 @@ Options:
|
|||
-e PATH, --extra-dir PATH
|
||||
-k PATH, --main-kit PATH
|
||||
-l PATH, --linux PATH
|
||||
-s PATH, --eset PATH
|
||||
-u PATH, --ufd-device PATH
|
||||
-w PATH, --winpe PATH
|
||||
|
||||
|
|
@ -569,7 +571,10 @@ def update_boot_entries(ufd_dev, images=None) -> None:
|
|||
'sed',
|
||||
'--in-place',
|
||||
'--regexp-extended',
|
||||
f's/___+/{uuids[0]}/',
|
||||
(
|
||||
f's/___+/{uuids[0]}/; '
|
||||
f's#by-label/(eSysRescueLiveCD|{ISO_LABEL}|{UFD_LABEL})#by-uuid/{uuids[0]}#'
|
||||
),
|
||||
*configs,
|
||||
]
|
||||
run_program(cmd)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ if os.name == 'nt':
|
|||
)
|
||||
else:
|
||||
# Example: "/home/tech/Logs"
|
||||
DEFAULT_LOG_DIR = f'{os.path.expanduser("~")}/Logs'
|
||||
DEFAULT_LOG_DIR = pathlib.Path('~/Logs').expanduser().resolve()
|
||||
DEFAULT_LOG_NAME = cfg.main.KIT_NAME_FULL
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -88,11 +88,15 @@ def mount_backup_shares(read_write: bool = False) -> list[str]:
|
|||
continue
|
||||
|
||||
# Mount share
|
||||
proc = mount_network_share(details, mount_point, read_write=read_write)
|
||||
if proc.returncode:
|
||||
try:
|
||||
proc = mount_network_share(details, mount_point, read_write=read_write)
|
||||
if proc.returncode:
|
||||
report.append(f'Failed to Mount {mount_str}')
|
||||
else:
|
||||
report.append(f'Mounted {mount_str}')
|
||||
except RuntimeError:
|
||||
# Assuming we're not connected to a network?
|
||||
report.append(f'Failed to Mount {mount_str}')
|
||||
else:
|
||||
report.append(f'Mounted {mount_str}')
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@
|
|||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
import plistlib
|
||||
import re
|
||||
|
||||
from wk import std
|
||||
from wk.exe import run_program
|
||||
from wk.hw.disk import Disk
|
||||
from wk.ui import cli
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
|
|
@ -32,6 +36,189 @@ def decode_smc_bytes(text) -> int:
|
|||
return result
|
||||
|
||||
|
||||
def get_apfs_volumes(device_path):
|
||||
"""Get APFS volumes contained in device_path, returns list."""
|
||||
volumes = []
|
||||
containers = []
|
||||
|
||||
# Get APFS details
|
||||
cmd = ['diskutil', 'apfs', 'list', '-plist']
|
||||
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||
try:
|
||||
plist_data = plistlib.loads(proc.stdout)
|
||||
except (TypeError, ValueError):
|
||||
# Invalid / corrupt plist data? return empty dict to avoid crash
|
||||
LOG.error('Failed to get diskutil apfs list for %s', device_path)
|
||||
|
||||
# Find container(s) relating to device_path
|
||||
for container in plist_data['Containers']:
|
||||
if container['DesignatedPhysicalStore'] == device_path:
|
||||
containers.append(container)
|
||||
|
||||
# Add volumes
|
||||
for container in containers:
|
||||
for volume in container['Volumes']:
|
||||
mount_volume(volume['DeviceIdentifier'])
|
||||
volumes.append(Disk(f'/dev/{volume["DeviceIdentifier"]}'))
|
||||
|
||||
# Done
|
||||
return volumes
|
||||
|
||||
|
||||
def get_core_storage_volumes(device_path):
|
||||
"""Get CoreStorage volumes contained in device_path, returns list."""
|
||||
disks = []
|
||||
volumes = []
|
||||
|
||||
# Get coreStorage details
|
||||
cmd = ['diskutil', 'corestorage', 'list', '-plist']
|
||||
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||
try:
|
||||
plist_data = plistlib.loads(proc.stdout)
|
||||
except (TypeError, ValueError):
|
||||
# invalid / corrupt plist data? return empty dict to avoid crash
|
||||
LOG.error('failed to get diskutil corestorage list for %s', device_path)
|
||||
|
||||
# Find related virtual disks
|
||||
for l_vg in plist_data['CoreStorageLogicalVolumeGroups']:
|
||||
related = False
|
||||
|
||||
# Compare parent physical volumes against device_path
|
||||
for p_v in l_vg['CoreStoragePhysicalVolumes']:
|
||||
uuid = p_v['CoreStorageUUID']
|
||||
cmd = ['diskutil', 'coreStorage', 'info', '-plist', uuid]
|
||||
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||
try:
|
||||
plist_data = plistlib.loads(proc.stdout)
|
||||
except (TypeError, ValueError):
|
||||
LOG.error('failed to get diskutil corestorage info for %s', uuid)
|
||||
continue
|
||||
if plist_data['DeviceIdentifier'] == device_path:
|
||||
related = True
|
||||
break
|
||||
|
||||
# Move on if no related p_v was found
|
||||
if not related:
|
||||
continue
|
||||
|
||||
# Add logical disks to list
|
||||
for l_vf in l_vg['CoreStorageLogicalVolumeFamilies']:
|
||||
for l_v in l_vf['CoreStorageLogicalVolumes']:
|
||||
uuid = l_v['CoreStorageUUID']
|
||||
cmd = ['diskutil', 'coreStorage', 'info', '-plist', uuid]
|
||||
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||
try:
|
||||
plist_data = plistlib.loads(proc.stdout)
|
||||
except (TypeError, ValueError):
|
||||
LOG.error('failed to get diskutil corestorage info for %s', uuid)
|
||||
continue
|
||||
disks.append(plist_data['DeviceIdentifier'])
|
||||
|
||||
# Get volumes from logical disks
|
||||
for disk in disks:
|
||||
cmd = ['diskutil', 'list', '-plist', f'/dev/{disk}']
|
||||
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||
try:
|
||||
plist_data = plistlib.loads(proc.stdout)
|
||||
disk_data = plist_data['AllDisksAndPartitions'][0]
|
||||
except (TypeError, ValueError):
|
||||
LOG.error('Failed to get diskutil list for %s', disk)
|
||||
continue
|
||||
if 'Partitions' in disk_data:
|
||||
for part in disk_data['Partitions']:
|
||||
mount_volume(part['DeviceIdentifier'])
|
||||
volumes.append(Disk(f'/dev/{part["DeviceIdentifier"]}'))
|
||||
elif 'VolumeName' in disk_data:
|
||||
mount_volume(disk_data['DeviceIdentifier'])
|
||||
volumes.append(Disk(f'/dev/{disk_data["DeviceIdentifier"]}'))
|
||||
|
||||
# Done
|
||||
return volumes
|
||||
|
||||
|
||||
def mount_volume(volume_path, read_only=True):
|
||||
"""Try to mount a volume, returns subprocess.CompletedProcess."""
|
||||
cmd = ['sudo', 'diskutil', 'mount']
|
||||
if read_only:
|
||||
cmd.append('readOnly')
|
||||
cmd.append(volume_path)
|
||||
proc = run_program(cmd, check=False)
|
||||
|
||||
# Done
|
||||
return proc
|
||||
|
||||
|
||||
def mount_disk(device_path=None):
|
||||
"""Mount all detected volumes, returns list.
|
||||
|
||||
NOTE: If device_path is specified then only volumes
|
||||
under that path will be mounted.
|
||||
"""
|
||||
report = []
|
||||
volumes = []
|
||||
|
||||
# Get device details
|
||||
cmd = [
|
||||
'diskutil',
|
||||
'list',
|
||||
'-plist',
|
||||
]
|
||||
if device_path:
|
||||
cmd.append(device_path)
|
||||
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||
try:
|
||||
plist_data = plistlib.loads(proc.stdout)
|
||||
except (TypeError, ValueError):
|
||||
# Invalid / corrupt plist data? return empty dict to avoid crash
|
||||
LOG.error('Failed to get diskutil list for %s', device_path)
|
||||
return report
|
||||
|
||||
# Mount and add volumes
|
||||
for part in plist_data['AllDisksAndPartitions'][0]['Partitions']:
|
||||
proc = mount_volume(part['DeviceIdentifier'])
|
||||
|
||||
# Add volume
|
||||
volumes.append(Disk(f'/dev/{part["DeviceIdentifier"]}'))
|
||||
|
||||
# Add sub-volumes
|
||||
if proc.returncode:
|
||||
if part['Content'] == 'Apple_CoreStorage':
|
||||
volumes.extend(get_core_storage_volumes(part['DeviceIdentifier']))
|
||||
elif part['Content'] == 'Apple_APFS':
|
||||
volumes.extend(get_apfs_volumes(part['DeviceIdentifier']))
|
||||
|
||||
# Build report from volume list
|
||||
for vol in volumes:
|
||||
result = f'{vol.details["name"]:<20}'
|
||||
|
||||
# Containers
|
||||
if vol.details['Content'] in ('Apple_APFS', 'Apple_CoreStorage'):
|
||||
result += f'{vol.details["Content"].replace("Apple_", "")} container'
|
||||
report.append(cli.color_string(result, 'BLUE'))
|
||||
continue
|
||||
|
||||
# Unknown partitions
|
||||
if not vol.details['mountpoint']:
|
||||
result += 'Failed to mount'
|
||||
report.append(cli.color_string(result, 'RED'))
|
||||
continue
|
||||
|
||||
# Volumes
|
||||
result += f'{"Mounted on "+vol.details.get("mountpoint", "?"):<40}'
|
||||
size = vol.details.get('TotalSize', -1)
|
||||
free = vol.details.get('FreeSpace', -1)
|
||||
used = size - free
|
||||
result = (
|
||||
f'{result} ({vol.details.get("fstype", "Unknown FS")+",":<5} '
|
||||
f'{std.bytes_to_string(used, decimals=1):>9} used, '
|
||||
f'{std.bytes_to_string(free, decimals=1):>9} free) '
|
||||
)
|
||||
report.append(result)
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
||||
|
||||
def set_fans(mode) -> None:
|
||||
"""Set fans to auto or max."""
|
||||
if mode == 'auto':
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ from wk.cfg.windows_builds import (
|
|||
)
|
||||
from wk.exe import get_json_from_command, run_program, wait_for_procs
|
||||
from wk.kit.tools import find_kit_dir
|
||||
from wk.osticket import osTicket
|
||||
from wk.std import (
|
||||
GenericError,
|
||||
GenericWarning,
|
||||
|
|
@ -206,22 +207,65 @@ def check_4k_alignment(show_alert=False) -> list[str]:
|
|||
return report
|
||||
|
||||
|
||||
def defender_is_disabled():
|
||||
"""Check if Windows Defender is enabled, returns bool."""
|
||||
reg_key = r'Software\Microsoft\Windows Defender'
|
||||
disabled = reg_read_value('HKLM', reg_key, 'DisableAntiSpyware')
|
||||
disabled = disabled or reg_read_value('HKLM', reg_key, 'DisableAntiVirus')
|
||||
return bool(disabled)
|
||||
|
||||
|
||||
def export_bitlocker_info() -> None:
|
||||
"""Get Bitlocker info and save to the base directory of the kit."""
|
||||
commands = [
|
||||
['manage-bde', '-status', SYSTEMDRIVE],
|
||||
['manage-bde', '-protectors', '-get', SYSTEMDRIVE],
|
||||
]
|
||||
"""Get Bitlocker info and save to either the base directory of the kit or osTicket."""
|
||||
script_path = find_kit_dir('Scripts').joinpath('export_bitlocker.ps1')
|
||||
cmd = ['PowerShell', '-ExecutionPolicy', 'Bypass', '-File', script_path]
|
||||
file_name = ''
|
||||
output = []
|
||||
|
||||
# Get filename
|
||||
file_name = ui.input_text(prompt_msg='Enter filename')
|
||||
file_path = pathlib.Path(f'../../Bitlocker_{file_name}.txt').resolve()
|
||||
# Include OS info
|
||||
if os.environ.get('SYSTEMDRIVE', 'C:').upper() == 'X:':
|
||||
# Assuming this is true
|
||||
output.append('[Check run under Win10XPE]')
|
||||
else:
|
||||
output.append(get_os_name())
|
||||
output.append('')
|
||||
|
||||
# Save info
|
||||
# Get Bitlocker info
|
||||
proc = run_program(cmd, check=False)
|
||||
if proc.returncode or not proc.stdout.strip():
|
||||
output.append('Error: Failed to export Bitlocker info\n')
|
||||
output.extend(proc.stdout.splitlines())
|
||||
|
||||
# Show info
|
||||
print('\n'.join(output), '\n\n')
|
||||
|
||||
# Save to osTicket
|
||||
ost = osTicket()
|
||||
ost.init()
|
||||
ost.select_ticket()
|
||||
if not ost.disabled:
|
||||
ost.post_response('\n'.join(output))
|
||||
result = 'OK'
|
||||
if ost.disabled or ost.errors:
|
||||
result = 'Unknown'
|
||||
print(
|
||||
ansi.color_string(
|
||||
['\nPost info to osTicket... ', result],
|
||||
[None, 'GREEN' if result == 'OK' else 'YELLOW'],
|
||||
)
|
||||
)
|
||||
|
||||
# Save to file
|
||||
if ost.ticket_name:
|
||||
file_name = f'{ost.ticket_id}_{ost.ticket_name.replace(" ", "-")}'
|
||||
if not file_name:
|
||||
file_name = ui.input_text(prompt_msg='Enter filename: ', allow_empty=False)
|
||||
file_path = pathlib.Path(f'../../../Bitlocker_{file_name}.txt').resolve()
|
||||
with open(file_path, 'a', encoding='utf-8') as _f:
|
||||
for cmd in commands:
|
||||
proc = run_program(cmd, check=False)
|
||||
_f.write(f'{proc.stdout}\n\n')
|
||||
_f.write('\n'.join(output))
|
||||
|
||||
# Done
|
||||
ui.pause('\nPress Enter to exit...')
|
||||
|
||||
|
||||
def get_installed_antivirus() -> list[str]:
|
||||
|
|
|
|||
431
scripts/wk/osticket.py
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
"""WizardKit: osTicket Functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import atexit
|
||||
import logging
|
||||
import pathlib
|
||||
import time
|
||||
|
||||
import mariadb
|
||||
|
||||
from wk.cfg.hw import TESTSTATION_FILE
|
||||
from wk.cfg.osticket import SQL, STAFF
|
||||
from wk.ui import ansi, cli
|
||||
|
||||
|
||||
# STATIC_VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
FLAG_CODES = {
|
||||
'Pass': 1,
|
||||
'Fail': 2,
|
||||
}
|
||||
FLAG_CPU = 'zTemps'
|
||||
FLAG_DISK = 'zHDTune'
|
||||
FLAG_MAX_TEMP = 'zMaxTemp'
|
||||
RESPONSE_COLOR_CODES = {
|
||||
'Normal': '0',
|
||||
'Contact': '1',
|
||||
'Diags': '2',
|
||||
'Diags FAIL': '3',
|
||||
'Money': '4',
|
||||
}
|
||||
TABLE_RESPONSE = 'ost_ticket_response'
|
||||
TABLE_TICKET = 'ost_ticket'
|
||||
|
||||
|
||||
# Classes
|
||||
class osTicket():
|
||||
"""Class to track osTicket data and functions."""
|
||||
def __init__(self):
|
||||
self.db_connection = None
|
||||
self.db_cursor = None
|
||||
self.disabled: bool = False
|
||||
self.errors: bool = False
|
||||
self.note: str = ''
|
||||
self.ticket_id: int | None = None
|
||||
self.ticket_name: str | None = None
|
||||
self.ticket_subject: str | None = None
|
||||
|
||||
# Ensure connection is closed atexit
|
||||
atexit.register(self._disconnect)
|
||||
|
||||
def _connect(self, silent=True):
|
||||
"""Establish connection to osTicket."""
|
||||
if self.disabled:
|
||||
return
|
||||
|
||||
# Connect to database
|
||||
try:
|
||||
self.db_connection = mariadb.connect(
|
||||
host=SQL['Host'],
|
||||
port=SQL['Port'],
|
||||
database=SQL['DB'],
|
||||
user=SQL['User'],
|
||||
password=SQL['Pass'],
|
||||
connect_timeout=5,
|
||||
)
|
||||
self.db_cursor = self.db_connection.cursor()
|
||||
except mariadb.Error:
|
||||
# Assuming network issue or bad creds
|
||||
pass
|
||||
|
||||
# Raise exception if necessary
|
||||
if self.db_cursor is None:
|
||||
LOG.error('Failed to connect to osTicket database')
|
||||
if silent:
|
||||
# Don't raise exceptions, just disable ost
|
||||
self.disabled = True
|
||||
self.errors = True
|
||||
else:
|
||||
raise RuntimeError('Failed to connect to osTicket database')
|
||||
|
||||
def _disconnect(self):
|
||||
"""Close osTicket connection."""
|
||||
for db_obj in (self.db_cursor, self.db_connection):
|
||||
if db_obj:
|
||||
try:
|
||||
db_obj.close()
|
||||
except mariadb.Error:
|
||||
# Ignore errors since vars will be reset below
|
||||
pass
|
||||
|
||||
# Reset db objects
|
||||
self.db_cursor = None
|
||||
self.db_connection = None
|
||||
|
||||
def _get_flag(self, flag_name):
|
||||
"""Get flag for self.ticket_id from osTicket, returns str."""
|
||||
flag_value = None
|
||||
self._verify_ticket_id()
|
||||
|
||||
# Build SQL cmd
|
||||
sql_cmd = (
|
||||
f"SELECT `{flag_name}` FROM `{SQL['DB']}`.`{TABLE_TICKET}` "
|
||||
f"WHERE `{TABLE_TICKET}`.`ticket_id` = {self.ticket_id};"
|
||||
)
|
||||
|
||||
# Run SQL cmd
|
||||
try:
|
||||
self.db_cursor.execute(sql_cmd)
|
||||
for s in self.db_cursor:
|
||||
flag_value = s[0]
|
||||
except mariadb.Error as err_msg:
|
||||
cli.print_error(err_msg)
|
||||
self.errors = True
|
||||
|
||||
# Done
|
||||
return str(flag_value)
|
||||
|
||||
def _get_ticket_field(self, ticket_id, field_name):
|
||||
"""Get field for ticket_id from osTicket, returns str."""
|
||||
field_data = None
|
||||
|
||||
# Build SQL cmd
|
||||
sql_cmd = (
|
||||
f"SELECT {field_name} FROM `{SQL['DB']}`.`{TABLE_TICKET}` "
|
||||
f"WHERE `{TABLE_TICKET}`.`ticket_id` = {ticket_id};"
|
||||
)
|
||||
|
||||
# Lookup data
|
||||
# NOTE: If multiple entries are found it will return the last
|
||||
try:
|
||||
self.db_cursor.execute(sql_cmd)
|
||||
for result in self.db_cursor:
|
||||
field_data = result[0]
|
||||
except mariadb.Error as err_msg:
|
||||
# Show error and return None
|
||||
cli.print_error(err_msg)
|
||||
|
||||
# Done
|
||||
return field_data
|
||||
|
||||
def _set_flag(self, flag_name, flag_value):
|
||||
"""Set flag_name to flag_value for ticket_id in osTicket.
|
||||
|
||||
NOTE: This will overwrite any existing value.
|
||||
"""
|
||||
self._verify_ticket_id()
|
||||
sql_cmd = (
|
||||
f"UPDATE `{SQL['DB']}`.`{TABLE_TICKET}` "
|
||||
f"SET `{flag_name}` = '{flag_value}' "
|
||||
f"WHERE `{TABLE_TICKET}`.`ticket_id` = {self.ticket_id};"
|
||||
)
|
||||
|
||||
# Run SQL cmd
|
||||
try:
|
||||
self.db_cursor.execute(sql_cmd)
|
||||
except mariadb.Error as err_msg:
|
||||
cli.print_error(err_msg)
|
||||
self.errors = True
|
||||
|
||||
def _verify_ticket_id(self):
|
||||
"""Verify that ticket_id has been set."""
|
||||
if not self.ticket_id:
|
||||
LOG.error('Ticket ID not set')
|
||||
raise RuntimeError('Ticket ID not set')
|
||||
|
||||
def add_note(self, prompt: str = 'Add note') -> list[str]:
|
||||
"""Add note to be included in osTicket replies."""
|
||||
lines = []
|
||||
if not prompt:
|
||||
prompt = 'Please enter any additional information for this ticket'
|
||||
|
||||
# Instructions
|
||||
cli.print_standard(prompt)
|
||||
cli.print_info(' (End note with an empty line)')
|
||||
cli.print_standard(' ')
|
||||
|
||||
# Get note
|
||||
while True:
|
||||
text = cli.input_text('> ', allow_empty=True)
|
||||
if not text:
|
||||
break
|
||||
lines.append(text.strip())
|
||||
|
||||
# Save note
|
||||
if lines:
|
||||
self.note = lines[0]
|
||||
for line in lines[1:]:
|
||||
self.note += f'\n...{line}'
|
||||
else:
|
||||
self.note = ''
|
||||
|
||||
# Done
|
||||
return lines
|
||||
|
||||
def init(self):
|
||||
"""Revert to defaults."""
|
||||
self._disconnect()
|
||||
self.disabled = False
|
||||
self.errors = False
|
||||
self.note = None
|
||||
self.ticket_id = None
|
||||
self.ticket_name = None
|
||||
|
||||
def post_response(self, response, color='Normal'):
|
||||
"""Post a reply to a ticket in osTicket."""
|
||||
cur_date_time = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
lines = []
|
||||
test_station = get_test_station_name()
|
||||
self._connect(silent=True)
|
||||
self._verify_ticket_id()
|
||||
|
||||
# Bail if disabled
|
||||
if self.disabled:
|
||||
return
|
||||
|
||||
# Format response
|
||||
if test_station:
|
||||
lines.append(f'[Test-Station: {test_station}]')
|
||||
lines.append(f'[Report for ticket #{self.ticket_id} {self.ticket_name}]')
|
||||
if self.note:
|
||||
lines.append(f'[Note] {self.note}\n')
|
||||
lines.append(str(response))
|
||||
response = '\n'.join(lines)
|
||||
response = ansi.strip_colors(response)
|
||||
response = response.replace("`", "'").replace("'", "\\'")
|
||||
|
||||
# Build SQL cmd
|
||||
sql_cmd = (
|
||||
f"INSERT INTO `{SQL['DB']}`.`{TABLE_RESPONSE}` "
|
||||
f"(ticket_id, staff_id, staff_name, response, created, code) "
|
||||
f"VALUES ("
|
||||
f" '{self.ticket_id}',"
|
||||
f" '{STAFF['ID']}',"
|
||||
f" '{STAFF['Name']}',"
|
||||
f" '{response}',"
|
||||
f" '{cur_date_time}',"
|
||||
f" '{RESPONSE_COLOR_CODES.get(color, 'Normal')}'"
|
||||
f");"
|
||||
)
|
||||
|
||||
# Run SQL cmd
|
||||
try:
|
||||
self.db_cursor.execute(sql_cmd)
|
||||
except mariadb.Error:
|
||||
self.errors = True
|
||||
|
||||
# Update ticket last repsonse field
|
||||
sql_cmd = (
|
||||
f"UPDATE `{SQL['DB']}`.`{TABLE_TICKET}`"
|
||||
f"SET `lastresponse` = '{cur_date_time}' "
|
||||
f"WHERE `{TABLE_TICKET}`.`ticket_id` = {self.ticket_id};"
|
||||
)
|
||||
try:
|
||||
self.db_cursor.execute(sql_cmd)
|
||||
except mariadb.Error:
|
||||
self.errors = True
|
||||
|
||||
# Done
|
||||
self._disconnect()
|
||||
|
||||
def select_ticket(self):
|
||||
"""Set ticket number and name from osTicket DB."""
|
||||
cli.print_standard('Connecting to osTicket...')
|
||||
|
||||
# Bail if disabled
|
||||
if self.disabled:
|
||||
return
|
||||
|
||||
# Connect
|
||||
while True:
|
||||
try:
|
||||
self._connect(silent=False)
|
||||
except (mariadb.Error, RuntimeError):
|
||||
cli.print_warning('Failed to connect to osTicket')
|
||||
if not cli.ask('Try again?'):
|
||||
cli.print_standard('Integration disabled for this session')
|
||||
self.disabled = True
|
||||
return
|
||||
else:
|
||||
# Connection successful
|
||||
break
|
||||
|
||||
# Main loop
|
||||
while self.ticket_id is None:
|
||||
cli.print_standard(' ')
|
||||
_id = cli.input_text(
|
||||
'Enter ticket number (or leave blank to disable): ', allow_empty=True,
|
||||
)
|
||||
_id = _id.strip()
|
||||
|
||||
# Nothing entered
|
||||
if not _id:
|
||||
print(' ')
|
||||
if cli.ask('Disable osTicket integration for this session?'):
|
||||
self.disabled = True
|
||||
break
|
||||
|
||||
# Invalid ID entered
|
||||
if not _id.isnumeric():
|
||||
continue
|
||||
|
||||
# Valid ID entered, lookup name
|
||||
_name = self._get_ticket_field(_id, 'name')
|
||||
|
||||
# Invalid ticket selected
|
||||
if _name is None:
|
||||
cli.print_error(f'Ticket #{_id} not found')
|
||||
continue
|
||||
|
||||
# Valid ticket selected, lookup subject
|
||||
_subject = self._get_ticket_field(_id, 'subject')
|
||||
|
||||
# Verify selection
|
||||
cli.print_colored(
|
||||
['You have selected ticket', f'#{_id}', _name],
|
||||
[None, 'BLUE', None],
|
||||
)
|
||||
cli.print_colored(f' {_subject}', 'CYAN')
|
||||
cli.print_standard(' ')
|
||||
if cli.ask('Is this correct?'):
|
||||
self.ticket_id = _id
|
||||
self.ticket_name = _name
|
||||
self.ticket_subject = _subject
|
||||
|
||||
# Done
|
||||
self._disconnect()
|
||||
|
||||
def set_cpu_max_temp(self, temp):
|
||||
"""Set CPU max temp in osTicket for ticket_id.
|
||||
|
||||
NOTE: This will not replace a higher temp value.
|
||||
"""
|
||||
LOG.info('Setting max CPU temp to %s', temp)
|
||||
self._connect(silent=True)
|
||||
|
||||
# Bail if disabled
|
||||
if self.disabled:
|
||||
return
|
||||
|
||||
# Compare to current temp
|
||||
current_temp = self._get_flag(FLAG_MAX_TEMP)
|
||||
if str(current_temp).isnumeric() and int(current_temp) > temp:
|
||||
cli.print_warning('Not replacing higher temp in osTicket')
|
||||
self._disconnect()
|
||||
return
|
||||
|
||||
# Update temp
|
||||
self._set_flag(FLAG_MAX_TEMP, int(temp))
|
||||
|
||||
# Done
|
||||
self._disconnect()
|
||||
|
||||
def set_flag_failed(self, flag_name):
|
||||
"""Set flag as failed in osTicket for ticket_id."""
|
||||
LOG.warning('Setting osTicket %s checkbox to FAILED', flag_name)
|
||||
real_flag_name = FLAG_CPU if flag_name == 'CPU' else FLAG_DISK
|
||||
self._connect(silent=True)
|
||||
|
||||
# Bail if disabled
|
||||
if self.disabled:
|
||||
return
|
||||
|
||||
# Set flag
|
||||
self._set_flag(real_flag_name, FLAG_CODES['Fail'])
|
||||
|
||||
# Done
|
||||
self._disconnect()
|
||||
|
||||
def set_flag_passed(self, flag_name):
|
||||
"""Set flag as passed in osTicket for ticket_id.
|
||||
|
||||
NOTE: This will not overwrite a failed status.
|
||||
"""
|
||||
real_flag_name = FLAG_CPU if flag_name == 'CPU' else FLAG_DISK
|
||||
self._connect(silent=True)
|
||||
|
||||
# Bail if disabled
|
||||
if self.disabled:
|
||||
return
|
||||
|
||||
# Bail if flag checkbox set as FAILED
|
||||
if self._get_flag(real_flag_name) == str(FLAG_CODES['Fail']):
|
||||
cli.print_warning(
|
||||
f'Not replacing osTicket {flag_name} checkbox FAILED value',
|
||||
)
|
||||
self._disconnect()
|
||||
return
|
||||
|
||||
# Current value != to FAILED, updating checkbox
|
||||
LOG.info('Setting osTicket %s checkbox to PASSED', flag_name)
|
||||
self._set_flag(real_flag_name, FLAG_CODES['Pass'])
|
||||
|
||||
# Done
|
||||
self._disconnect()
|
||||
|
||||
|
||||
# Functions
|
||||
def get_test_station_name():
|
||||
"""Get test station name, returns str."""
|
||||
hostname_file = pathlib.Path(TESTSTATION_FILE)
|
||||
|
||||
# Bail early
|
||||
if not hostname_file.exists():
|
||||
return ''
|
||||
if not hostname_file.read_text(encoding='utf-8'):
|
||||
return ''
|
||||
|
||||
# Done
|
||||
return hostname_file.read_text(encoding='utf-8').splitlines()[0]
|
||||
|
||||
|
||||
def pad_with_dots(text, pad_right=False):
|
||||
"""Replace space padding with dots, returns str.
|
||||
|
||||
NOTE: This is a dumb hack to better align text in osTicket.
|
||||
"""
|
||||
text = str(text)
|
||||
text = text.replace(' ', '..')
|
||||
if '.' in text:
|
||||
if pad_right:
|
||||
text = f'{text}.'
|
||||
else:
|
||||
text = f'.{text}'
|
||||
|
||||
# Done
|
||||
return text
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -30,6 +30,7 @@ from wk.exe import (
|
|||
get_procs,
|
||||
run_program,
|
||||
popen_program,
|
||||
set_proc_priority,
|
||||
wait_for_procs,
|
||||
)
|
||||
from wk.io import (
|
||||
|
|
@ -39,14 +40,22 @@ from wk.io import (
|
|||
non_clobber_path,
|
||||
rename_item,
|
||||
)
|
||||
from wk.kit.tools import (download_tool, get_tool_path, run_tool)
|
||||
from wk.kit.tools import (
|
||||
download_tool,
|
||||
extract_archive,
|
||||
get_tool_path,
|
||||
run_tool,
|
||||
)
|
||||
from wk.log import (
|
||||
format_log_path,
|
||||
get_root_logger_path,
|
||||
update_log_path,
|
||||
)
|
||||
from wk.os.win import (
|
||||
ARCH,
|
||||
OS_VERSION,
|
||||
defender_is_disabled,
|
||||
show_alert_box,
|
||||
get_timezone,
|
||||
set_timezone,
|
||||
reg_delete_value,
|
||||
|
|
@ -85,7 +94,16 @@ PROGRAMFILES_32 = os.environ.get(
|
|||
'PROGRAMFILES', r'C:\Program Files (x86)',
|
||||
),
|
||||
)
|
||||
MBAM_EXE_PATH = 'Malwarebytes/Anti-Malware/mbam.exe'
|
||||
MBAM_PRESERVE_MARKER = 'Preserve-MBAM.marker'
|
||||
MBAM_UNINSTALL_KEY = (
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Uninstall'
|
||||
r'\{35065F43-4BB2-439A-BFF7-0F1014F2E0CD}_is1'
|
||||
)
|
||||
MS_ANTIVIRUS_ENABLED = 0x1000
|
||||
PROGRAMDATA = os.environ.get('{ALLUSERSPROFILE}', r'C:\ProgramData')
|
||||
SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE', 'C:')
|
||||
EMSISOFT_INSTALL_PATH = get_path_obj(f'{SYSTEMDRIVE}/EmsisoftCmd')
|
||||
WHITELIST = '\n'.join((
|
||||
str(CONEMU_EXE),
|
||||
fr'{PROGRAMFILES_32}\TeamViewer\TeamViewer.exe',
|
||||
|
|
@ -171,22 +189,17 @@ def build_menus(base_menus, title, presets) -> dict[str, ui.Menu]:
|
|||
return menus
|
||||
|
||||
|
||||
def update_scheduled_task() -> None:
|
||||
"""Create (or update) scheduled task to start repairs."""
|
||||
cmd = [
|
||||
'schtasks', '/create', '/f',
|
||||
'/sc', 'ONLOGON',
|
||||
'/tn', f'{KIT_NAME_FULL}-AutoRepairs',
|
||||
'/rl', 'HIGHEST',
|
||||
'/tr', fr'C:\Windows\System32\cmd.exe "/C {sys.executable} {sys.argv[0]}"',
|
||||
]
|
||||
if IN_CONEMU:
|
||||
cmd[-1] = f'{CONEMU_EXE} -run {sys.executable} {sys.argv[0]}'
|
||||
run_program(cmd)
|
||||
|
||||
|
||||
def end_session() -> None:
|
||||
def end_session(menus: dict[str, ui.Menu]) -> None:
|
||||
"""End Auto Repairs session."""
|
||||
# Add last run marker
|
||||
reg_set_value(
|
||||
'HKCU',
|
||||
fr'Software\{KIT_NAME_FULL}',
|
||||
'AutoRepairsLastRun',
|
||||
time.strftime('%Y-%m-%d'),
|
||||
'SZ',
|
||||
)
|
||||
|
||||
# Remove logon task
|
||||
cmd = [
|
||||
'schtasks', '/delete', '/f',
|
||||
|
|
@ -218,10 +231,18 @@ def end_session() -> None:
|
|||
except FileNotFoundError:
|
||||
LOG.error('Ending repair session but session not started.')
|
||||
try:
|
||||
cmd = ['reg', 'delete', fr'HKCU\{AUTO_REPAIR_KEY}', '/f']
|
||||
run_program(cmd)
|
||||
except CalledProcessError:
|
||||
LOG.error('Failed to remove Auto Repairs session settings')
|
||||
reg_delete_value('HKCU', AUTO_REPAIR_KEY, 'Kill Explorer')
|
||||
except FileNotFoundError:
|
||||
# Ignore if not set
|
||||
pass
|
||||
for group in menus:
|
||||
try:
|
||||
cmd = ['reg', 'delete', fr'HKCU\{AUTO_REPAIR_KEY}\{group}', '/f']
|
||||
run_program(cmd)
|
||||
except CalledProcessError:
|
||||
LOG.error(
|
||||
'Failed to remove Auto Repairs session settings for group %s', group,
|
||||
)
|
||||
|
||||
|
||||
def get_entry_settings(group, name) -> dict[str, Any]:
|
||||
|
|
@ -248,11 +269,15 @@ def init(menus, presets) -> None:
|
|||
session_started = is_session_started()
|
||||
|
||||
# Check if autologon is needed
|
||||
if not session_started and is_autologon_enabled():
|
||||
LOG.warning('Skipping Autologon to preserve current settings.')
|
||||
menus['Options'].options['Use Autologon']['Disabled'] = True
|
||||
menus['Options'].options['Use Autologon']['Selected'] = False
|
||||
reg_set_value('HKCU', AUTO_REPAIR_KEY, 'Use Autologon', 0, 'DWORD')
|
||||
if not session_started:
|
||||
if is_autologon_enabled():
|
||||
LOG.warning('Skipping Autologon to preserve current settings.')
|
||||
menus['Options'].options['Use Autologon']['Disabled'] = True
|
||||
menus['Options'].options['Use Autologon']['Selected'] = False
|
||||
reg_set_value('HKCU', AUTO_REPAIR_KEY, 'Use Autologon', 0, 'DWORD')
|
||||
else:
|
||||
# Deselect Autologon by default
|
||||
menus['Options'].options['Use Autologon']['Selected'] = False
|
||||
save_selection_settings(menus)
|
||||
|
||||
# Start new session
|
||||
|
|
@ -274,6 +299,7 @@ def init_run(options) -> None:
|
|||
update_scheduled_task()
|
||||
if options['Kill Explorer']['Selected']:
|
||||
atexit.register(start_explorer)
|
||||
reg_set_value('HKCU', AUTO_REPAIR_KEY, 'Kill Explorer', 1, 'DWORD')
|
||||
TRY_PRINT.run('Killing Explorer...', kill_explorer, msg_good='DONE')
|
||||
if options['Use Autologon']['Selected'] and not is_autologon_enabled():
|
||||
TRY_PRINT.run(
|
||||
|
|
@ -318,7 +344,6 @@ def init_session(options) -> None:
|
|||
download=True, msg_good='DONE',
|
||||
)
|
||||
print('')
|
||||
reboot(30)
|
||||
|
||||
|
||||
def is_autologon_enabled() -> bool:
|
||||
|
|
@ -415,7 +440,7 @@ def run_auto_repairs(base_menus, presets) -> None:
|
|||
show_main_menu(base_menus, menus, presets, title)
|
||||
except SystemExit:
|
||||
if ui.ask('End session?'):
|
||||
end_session()
|
||||
end_session(menus)
|
||||
raise
|
||||
|
||||
# Start or resume repairs
|
||||
|
|
@ -440,7 +465,7 @@ def run_auto_repairs(base_menus, presets) -> None:
|
|||
ui.abort()
|
||||
|
||||
# Done
|
||||
end_session()
|
||||
end_session(menus)
|
||||
ui.print_info('Done')
|
||||
ui.pause('Press Enter to exit...')
|
||||
|
||||
|
|
@ -610,6 +635,20 @@ def update_main_menu(menus) -> None:
|
|||
menus['Main'].options[name]['Display Name'] = display_name
|
||||
|
||||
|
||||
def update_scheduled_task():
|
||||
"""Create (or update) scheduled task to start repairs."""
|
||||
cmd = [
|
||||
'schtasks', '/create', '/f',
|
||||
'/sc', 'ONLOGON',
|
||||
'/tn', f'{KIT_NAME_FULL}-AutoRepairs',
|
||||
'/rl', 'HIGHEST',
|
||||
'/tr', fr'C:\Windows\System32\cmd.exe "/C {sys.executable} {sys.argv[0]}"',
|
||||
]
|
||||
if IN_CONEMU:
|
||||
cmd[-1] = f'{CONEMU_EXE} -run {sys.executable} {sys.argv[0]}'
|
||||
run_program(cmd)
|
||||
|
||||
|
||||
# Auto Repairs: Wrapper Functions
|
||||
def auto_adwcleaner(group, name) -> None:
|
||||
"""Run AdwCleaner scan.
|
||||
|
|
@ -681,6 +720,28 @@ def auto_chkdsk(group, name) -> None:
|
|||
reboot()
|
||||
|
||||
|
||||
def auto_disable_defender(group, name):
|
||||
"""Open Windows Defender Threat Settings."""
|
||||
result = TRY_PRINT.run(
|
||||
'Open Windows Defender settings...',
|
||||
open_defender_settings,
|
||||
disable=True,
|
||||
msg_good='Done',
|
||||
)
|
||||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_enable_defender(group, name):
|
||||
"""Open Windows Defender Threat Settings."""
|
||||
result = TRY_PRINT.run(
|
||||
'Open Windows Defender settings...',
|
||||
open_defender_settings,
|
||||
enable=True,
|
||||
msg_good='Done',
|
||||
)
|
||||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_disable_pending_renames(group, name) -> None:
|
||||
"""Disable pending renames."""
|
||||
result = TRY_PRINT.run(
|
||||
|
|
@ -714,6 +775,21 @@ def auto_dism(group, name) -> None:
|
|||
reboot()
|
||||
|
||||
|
||||
def auto_emsisoft_cmd_run(group, name):
|
||||
"""Run EmisoftCmd."""
|
||||
TRY_PRINT.run('EmsisoftCmd (Install)...', install_emsisoft_cmd)
|
||||
TRY_PRINT.run('EmsisoftCmd (Update)...', update_emsisoft_cmd)
|
||||
result = TRY_PRINT.run('EmsisoftCmd (Scan)...', run_emsisoft_cmd_scan)
|
||||
TRY_PRINT.run('EmsisoftCmd (Uninstall Service)...', delete_emsisoft_cmd_service)
|
||||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_emsisoft_cmd_uninstall(group, name):
|
||||
"""Uninstall EmsisoftCmd."""
|
||||
result = TRY_PRINT.run('EmsisoftCmd (Uninstall)...', uninstall_emsisoft_cmd)
|
||||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_enable_regback(group, name) -> None:
|
||||
"""Enable RegBack."""
|
||||
result = TRY_PRINT.run(
|
||||
|
|
@ -724,6 +800,14 @@ def auto_enable_regback(group, name) -> None:
|
|||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_fix_file_associations(group, name):
|
||||
"""Run Fix File Associations scan."""
|
||||
result = TRY_PRINT.run(
|
||||
'Fix File Associations...', fix_file_associations, msg_good='DONE',
|
||||
)
|
||||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_hitmanpro(group, name) -> None:
|
||||
"""Run HitmanPro scan."""
|
||||
result = TRY_PRINT.run('HitmanPro...', run_hitmanpro, msg_good='DONE')
|
||||
|
|
@ -736,6 +820,29 @@ def auto_kvrt(group, name) -> None:
|
|||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_mbam_install(group, name):
|
||||
"""Install Malwarebytes."""
|
||||
result = TRY_PRINT.run('Malwarebytes (Install)...', install_mbam)
|
||||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_mbam_run(group, name):
|
||||
"""Run Malwarebytes.
|
||||
|
||||
save_settings() is called first since MBAM may kill this script.
|
||||
"""
|
||||
save_settings(group, name, done=True, failed=False, message='DONE')
|
||||
result = TRY_PRINT.run('Malwarebytes (Run)...', run_mbam, msg_good='DONE')
|
||||
ui.pause('Press Enter to continue...')
|
||||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_mbam_uninstall(group, name):
|
||||
"""Uninstall Malwarebytes."""
|
||||
result = TRY_PRINT.run('Malwarebytes (Uninstall)...', uninstall_mbam)
|
||||
save_settings(group, name, result=result)
|
||||
|
||||
|
||||
def auto_microsoft_defender(group, name) -> None:
|
||||
"""Run Microsoft Defender scan."""
|
||||
result = TRY_PRINT.run(
|
||||
|
|
@ -755,6 +862,7 @@ def auto_remove_power_plan(group, name) -> None:
|
|||
"""Remove custom power plan and set to Balanced."""
|
||||
result = TRY_PRINT.run(
|
||||
'Remove Custom Power Plan...', remove_custom_power_plan,
|
||||
high_performance=True,
|
||||
)
|
||||
save_settings(group, name, result=result)
|
||||
|
||||
|
|
@ -983,11 +1091,61 @@ def backup_registry() -> None:
|
|||
run_tool('ERUNT', 'ERUNT', backup_path, 'sysreg', 'curuser', 'otherusers')
|
||||
|
||||
|
||||
def delete_emsisoft_cmd_service():
|
||||
"""Delete EmsisoftCmd service."""
|
||||
try:
|
||||
stop_service('epp')
|
||||
except GenericError:
|
||||
pass
|
||||
|
||||
# Delete service
|
||||
run_program(['sc', 'delete', 'epp'], check=False)
|
||||
|
||||
|
||||
def delete_registry_null_keys() -> None:
|
||||
"""Delete registry keys with embedded null characters."""
|
||||
run_tool('RegDelNull', 'RegDelNull', '-s', '-y', download=True)
|
||||
|
||||
|
||||
def fix_file_associations():
|
||||
"""Fix file associations via registry edits."""
|
||||
winaio_path = get_tool_path('WinRepairAIO', 'Repair_Windows').parent
|
||||
winaio_path = winaio_path.joinpath(
|
||||
f'Files/regfiles/file_associations/{OS_VERSION}',
|
||||
)
|
||||
for item in winaio_path.iterdir():
|
||||
if item.suffix.lower() != '.reg':
|
||||
continue
|
||||
cmd = ['reg', 'import', str(item), f'/reg:{ARCH}']
|
||||
run_program(cmd, check=False)
|
||||
|
||||
|
||||
def install_emsisoft_cmd():
|
||||
"""Install EmsisoftCmd."""
|
||||
download_tool('EmsisoftCmd', 'EmsisoftCmd')
|
||||
installer = get_tool_path('EmsisoftCmd', 'EmsisoftCmd')
|
||||
extract_archive(installer, EMSISOFT_INSTALL_PATH, '-aos')
|
||||
|
||||
|
||||
def install_mbam():
|
||||
"""Install Malwarebytes."""
|
||||
marker = set_local_storage_path('.', MBAM_PRESERVE_MARKER)
|
||||
marker.unlink(missing_ok=True)
|
||||
|
||||
# Check for current installation
|
||||
for path in ('ProgramW6432', 'PROGRAMFILES', 'PROGRAMFILES(X86)'):
|
||||
if os.path.exists(f'{os.environ.get(path, "")}/{MBAM_EXE_PATH}'):
|
||||
LOG.info('Previous Malwarebytes installation detected.')
|
||||
marker.touch()
|
||||
break
|
||||
|
||||
# Install / Upgrade
|
||||
proc = run_tool(
|
||||
'MBAM', 'MBAM', '/VERYSILENT', '/NORESTART', download=True, popen=True,
|
||||
)
|
||||
proc.wait()
|
||||
|
||||
|
||||
def log_kvrt_results(log_path, report_path) -> None:
|
||||
"""Parse KVRT report and log results in plain text."""
|
||||
log_text = ''
|
||||
|
|
@ -1058,6 +1216,41 @@ def run_bleachbit(cleaners, preview=True) -> None:
|
|||
)
|
||||
|
||||
|
||||
def run_emsisoft_cmd_scan():
|
||||
"""Run EmsisoftCmd scan."""
|
||||
log_path = format_log_path(
|
||||
log_name='EmsisoftCmd', timestamp=True, tool=True,
|
||||
)
|
||||
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
quarantine_path = set_quarantine_path('EmsisoftCmd')
|
||||
quarantine_path.mkdir(parents=True, exist_ok=True)
|
||||
whitelist_path = log_path.with_suffix('.wl')
|
||||
|
||||
# Create whitelist
|
||||
whitelist_path.write_text(WHITELIST, encoding='utf-8')
|
||||
|
||||
# Run Scan
|
||||
cmd = [
|
||||
f'{EMSISOFT_INSTALL_PATH}/a2cmd.exe',
|
||||
fr'/files={PROGRAMDATA},{SYSTEMDRIVE}\Users',
|
||||
'/archive', '/memory', '/ntfs', '/pup', '/traces',
|
||||
f'/log={log_path}',
|
||||
f'/quarantine={quarantine_path}',
|
||||
f'/whitelist={whitelist_path}',
|
||||
]
|
||||
if IN_CONEMU:
|
||||
cmd.extend(['-new_console:nb', '-new_console:s33V'])
|
||||
run_program(cmd, check=False, pipe=False)
|
||||
sleep(5)
|
||||
set_proc_priority('a2cmd.exe', 'HIGH')
|
||||
wait_for_procs('a2cmd.exe')
|
||||
else:
|
||||
proc = popen_program(cmd, priority=True)
|
||||
sleep(5)
|
||||
set_proc_priority('a2cmd.exe', 'HIGH')
|
||||
proc.wait()
|
||||
|
||||
|
||||
def run_bcuninstaller() -> None:
|
||||
"""Run BCUninstaller."""
|
||||
run_tool('BCUninstaller', 'BCUninstaller')
|
||||
|
|
@ -1069,7 +1262,11 @@ def run_hitmanpro() -> None:
|
|||
log_path = log_path.with_suffix('.xml')
|
||||
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
cmd_args = ['/scanonly', f'/log={log_path}']
|
||||
run_tool('HitmanPro', 'HitmanPro', *cmd_args, download=True)
|
||||
proc = run_tool(
|
||||
'HitmanPro', 'HitmanPro', *cmd_args,
|
||||
download=True, popen=True, priority=True,
|
||||
)
|
||||
proc.wait()
|
||||
|
||||
|
||||
def run_kvrt() -> None:
|
||||
|
|
@ -1103,16 +1300,41 @@ def run_kvrt() -> None:
|
|||
_f.write(f'"{kvrt_path}" {" ".join(cmd_args)}\n')
|
||||
cmd = ('cmd', '/c', tmp_file, '-new_console:nb', '-new_console:s33V')
|
||||
run_program(cmd, check=False)
|
||||
sleep(1)
|
||||
sleep(5)
|
||||
set_proc_priority('KVRT', 'HIGH', exact=False)
|
||||
wait_for_procs('KVRT.exe')
|
||||
log_kvrt_results(log_path, report_path)
|
||||
return
|
||||
|
||||
# Run in background
|
||||
run_tool('KVRT', 'KVRT', *cmd_args, download=True)
|
||||
proc = run_tool(
|
||||
'KVRT', 'KVRT', *cmd_args,
|
||||
download=True, popen=True, priority=True,
|
||||
)
|
||||
sleep(5)
|
||||
set_proc_priority('KVRT', 'HIGH', exact=False)
|
||||
proc.wait()
|
||||
log_kvrt_results(log_path, report_path)
|
||||
|
||||
|
||||
def run_mbam():
|
||||
"""Run Malwarebytes."""
|
||||
exe_path = None
|
||||
|
||||
# Get EXE path
|
||||
for path in ('ProgramW6432', 'PROGRAMFILES'):
|
||||
test_path = get_path_obj(f'{os.environ.get(path, "")}/{MBAM_EXE_PATH}')
|
||||
if test_path.exists():
|
||||
exe_path = str(test_path)
|
||||
|
||||
# Bail if MBAM not found
|
||||
if not exe_path:
|
||||
raise FileNotFoundError('MBAM not found')
|
||||
|
||||
# Run
|
||||
run_program(exe_path, check=False)
|
||||
|
||||
|
||||
def run_microsoft_defender(full=True) -> None:
|
||||
"""Run Microsoft Defender scan."""
|
||||
reg_key = r'Software\Microsoft\Windows Defender'
|
||||
|
|
@ -1129,8 +1351,7 @@ def run_microsoft_defender(full=True) -> None:
|
|||
# Get MS Defender status
|
||||
## NOTE: disabled may be set to an int instead of bool
|
||||
## This is fine because we're just checking if it's enabled.
|
||||
disabled = bool(reg_read_value('HKLM', reg_key, 'DisableAntiSpyware'))
|
||||
disabled = disabled or reg_read_value('HKLM', reg_key, 'DisableAntiVirus')
|
||||
disabled = defender_is_disabled()
|
||||
try:
|
||||
passive_mode = reg_read_value('HKLM', reg_key, 'PassiveMode') == 2
|
||||
except FileNotFoundError:
|
||||
|
|
@ -1172,6 +1393,38 @@ def run_rkill() -> None:
|
|||
run_tool('RKill', 'RKill', *cmd_args, download=True)
|
||||
|
||||
|
||||
def uninstall_emsisoft_cmd():
|
||||
"""Uninstall EmsisoftCmd."""
|
||||
delete_folder(EMSISOFT_INSTALL_PATH, force=True, ignore_errors=True)
|
||||
|
||||
|
||||
def uninstall_mbam():
|
||||
"""Uninstall Malwarebytes."""
|
||||
marker = set_local_storage_path('.', MBAM_PRESERVE_MARKER)
|
||||
if marker.exists():
|
||||
marker.unlink()
|
||||
raise GenericWarning('Leaving existing MBAM installation in place.')
|
||||
|
||||
# Uninstall
|
||||
install_path = reg_read_value('HKLM', MBAM_UNINSTALL_KEY, 'InstallLocation')
|
||||
cmd = [
|
||||
fr'{install_path}\mbuns.exe', '/Uninstall', '/VERYSILENT', '/NORESTART',
|
||||
]
|
||||
run_program(cmd)
|
||||
|
||||
|
||||
def update_emsisoft_cmd():
|
||||
"""Update EmsisoftCmd."""
|
||||
cmd = [f'{EMSISOFT_INSTALL_PATH}/a2cmd.exe', '/update']
|
||||
if IN_CONEMU:
|
||||
cmd.extend(['-new_console:nb', '-new_console:s33V'])
|
||||
run_program(cmd, check=False, pipe=False)
|
||||
sleep(1)
|
||||
wait_for_procs('a2cmd.exe')
|
||||
else:
|
||||
run_program(cmd, check=False)
|
||||
|
||||
|
||||
# OS Built-in Functions
|
||||
def create_custom_power_plan(enable_sleep=True, keep_display_on=False) -> None:
|
||||
"""Create new power plan and set as active."""
|
||||
|
|
@ -1293,6 +1546,63 @@ def kill_explorer() -> None:
|
|||
run_program(cmd, check=False)
|
||||
|
||||
|
||||
def open_defender_settings(disable=False, enable=False):
|
||||
"""Open Windows Defender Threat Settings."""
|
||||
enabled = None
|
||||
|
||||
# Check Registry if Defender is disabled
|
||||
if defender_is_disabled():
|
||||
raise GenericError('Defender is disabled.')
|
||||
|
||||
# Check WMIC if Defender is active
|
||||
cmd = [
|
||||
'WMIC', r'/namespace:\\root\SecurityCenter2',
|
||||
'path', 'AntivirusProduct',
|
||||
'where', 'displayName="Windows Defender"',
|
||||
'get', 'productState', '/value',
|
||||
]
|
||||
try:
|
||||
proc = run_program(cmd)
|
||||
status = proc.stdout.split('=')[1]
|
||||
enabled = bool(int(status) & MS_ANTIVIRUS_ENABLED)
|
||||
except Exception:
|
||||
# Unknown result, just show the prompt
|
||||
pass
|
||||
|
||||
# Set prompt message
|
||||
message = 'Please adjust Windows Defender settings as appropriate.'
|
||||
if disable:
|
||||
if enabled is False:
|
||||
# Already disabled, just bail
|
||||
return
|
||||
message = 'Please disable realtime Windows Defender scanning.'
|
||||
elif enable:
|
||||
if enabled:
|
||||
# Already enabled, just bail
|
||||
return
|
||||
message = 'Please enable realtime Windows Defender scanning.'
|
||||
message += '\nPress OK to continue repairs.'
|
||||
|
||||
# Check Kill Explorer setting
|
||||
cmd = ['start', '', 'windowsdefender://threatsettings']
|
||||
kill_explorer_proc = False
|
||||
try:
|
||||
kill_explorer_proc = reg_read_value(
|
||||
'HKCU', AUTO_REPAIR_KEY, 'Kill Explorer',
|
||||
)
|
||||
except FileNotFoundError:
|
||||
# Ignore if not set
|
||||
pass
|
||||
if kill_explorer_proc:
|
||||
# Explorer is needed to open the settings window, relaunch for now
|
||||
start_explorer()
|
||||
sleep(2)
|
||||
run_program(cmd, check=False, shell=True)
|
||||
show_alert_box(message=message, title='Windows Defender: Threat Settings')
|
||||
if kill_explorer_proc:
|
||||
kill_explorer()
|
||||
|
||||
|
||||
def reboot(timeout=10) -> None:
|
||||
"""Reboot the system."""
|
||||
atexit.unregister(start_explorer)
|
||||
|
|
@ -1494,6 +1804,11 @@ def run_sfc_scan() -> None:
|
|||
raise OSError
|
||||
|
||||
|
||||
def run_uninstallview() -> None:
|
||||
"""Run UninstallView."""
|
||||
run_tool('UninstallView', 'UninstallView')
|
||||
|
||||
|
||||
def set_system_restore_size(size=8) -> None:
|
||||
"""Set System Restore size."""
|
||||
cmd = [
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import configparser
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
|
@ -14,20 +16,23 @@ from wk.cfg.main import KIT_NAME_FULL
|
|||
from wk.cfg.setup import (
|
||||
BROWSER_PATHS,
|
||||
DISABLED_ENTRIES_WINDOWS_11,
|
||||
FAB_TIMEFRAME,
|
||||
LIBREOFFICE_XCU_DATA,
|
||||
REG_CHROME_UBLOCK_ORIGIN,
|
||||
REG_WINDOWS_EXPLORER,
|
||||
REG_OPEN_SHELL_SETTINGS,
|
||||
REG_ESET_NOD32_SETTINGS,
|
||||
REG_OPEN_SHELL_LOW_POWER_IDLE,
|
||||
REG_OPEN_SHELL_SETTINGS,
|
||||
REG_WINDOWS_EXPLORER,
|
||||
UBLOCK_ORIGIN_URLS,
|
||||
)
|
||||
from wk.exe import kill_procs, run_program, popen_program
|
||||
from wk.exe import get_procs, kill_procs, run_program, popen_program
|
||||
from wk.io import case_insensitive_path, get_path_obj
|
||||
from wk.kit.tools import (
|
||||
ARCH,
|
||||
extract_archive,
|
||||
extract_tool,
|
||||
find_kit_dir,
|
||||
get_sdio_path,
|
||||
get_tool_path,
|
||||
run_tool,
|
||||
)
|
||||
|
|
@ -45,6 +50,7 @@ from wk.os.win import (
|
|||
get_volume_usage,
|
||||
is_activated,
|
||||
is_secure_boot_enabled,
|
||||
reg_read_value,
|
||||
reg_set_value,
|
||||
reg_write_settings,
|
||||
stop_service,
|
||||
|
|
@ -160,6 +166,44 @@ def build_menus(base_menus, title, presets) -> dict[str, ui.Menu]:
|
|||
return menus
|
||||
|
||||
|
||||
def check_if_auto_repairs_are_needed():
|
||||
"""Check if Auto Repairs are needed based on last Fab run."""
|
||||
try:
|
||||
last_run = reg_read_value(
|
||||
'HKCU', fr'Software\{KIT_NAME_FULL}', 'FabLastRun',
|
||||
)
|
||||
except FileNotFoundError:
|
||||
# Assuming it isn't needed
|
||||
return
|
||||
fab_last_run = datetime.strptime(last_run, '%Y-%m-%d')
|
||||
|
||||
# Check if Auto Repairs was run after Fab
|
||||
auto_repairs_last_run = None
|
||||
try:
|
||||
last_run = reg_read_value(
|
||||
'HKCU', fr'Software\{KIT_NAME_FULL}', 'AutoRepairsLastRun',
|
||||
)
|
||||
auto_repairs_last_run = datetime.strptime(last_run, '%Y-%m-%d')
|
||||
except FileNotFoundError:
|
||||
# Assuming it wasn't
|
||||
pass
|
||||
|
||||
# Check date(s) and prompt tech if necessary
|
||||
if datetime.now() - fab_last_run < timedelta(days=FAB_TIMEFRAME):
|
||||
if auto_repairs_last_run and auto_repairs_last_run >= fab_last_run:
|
||||
# Auto Repairs shouldn't be needed
|
||||
return
|
||||
ui.print_warning('Fab was recently run, Auto Repairs may be needed.')
|
||||
selection = ui.choice(
|
||||
'Continue with Auto Setup, run Auto Repairs, or Quit?', ['C', 'R', 'Q'],
|
||||
)
|
||||
if selection == 'Q':
|
||||
ui.abort()
|
||||
if selection == 'R':
|
||||
auto_repairs_script = find_kit_dir('Scripts').joinpath('auto_repairs.py')
|
||||
os.execv(sys.executable, ['python', auto_repairs_script])
|
||||
|
||||
|
||||
def check_os_and_set_menu_title(title) -> str:
|
||||
"""Check OS version and update title for menus, returns str."""
|
||||
color = None
|
||||
|
|
@ -188,6 +232,9 @@ def check_os_and_set_menu_title(title) -> str:
|
|||
|
||||
def load_preset(menus, presets, title, enable_menu_exit=True) -> None:
|
||||
"""Load menu settings from preset and ask selection question(s)."""
|
||||
msp = False
|
||||
|
||||
# Menu exit entry
|
||||
if not enable_menu_exit:
|
||||
MENU_PRESETS.actions['Main Menu'].update({'Disabled':True, 'Hidden':True})
|
||||
|
||||
|
|
@ -212,8 +259,36 @@ def load_preset(menus, presets, title, enable_menu_exit=True) -> None:
|
|||
ui.clear_screen()
|
||||
ui.print_standard(f'{title}')
|
||||
print('')
|
||||
if selection[0] == 'Default' and ui.ask('Install LibreOffice?'):
|
||||
menus['Install Software'].options['LibreOffice']['Selected'] = True
|
||||
if selection[0] == 'Default':
|
||||
# OpenShell
|
||||
if OS_VERSION != 11 and ui.ask('Install OpenShell?'):
|
||||
menus['Install Software'].options['Open-Shell']['Selected'] = True
|
||||
menus['Configure System'].options['Open-Shell']['Selected'] = True
|
||||
|
||||
# LibreOffice
|
||||
if ui.ask('Install LibreOffice?'):
|
||||
menus['Install Software'].options['LibreOffice']['Selected'] = True
|
||||
|
||||
# Hiberboot & Hibernation
|
||||
print('')
|
||||
msg = 'Disable Fast Startup and enable Hibernation? (Recommended for SSDs)'
|
||||
if ui.ask(msg):
|
||||
for option in ('Disable Fast Startup', 'Enable Hibernation'):
|
||||
menus['Configure System'].options[option]['Selected'] = True
|
||||
|
||||
# Apply ITS settings?
|
||||
msp = ui.ask('Is this an ITS system?')
|
||||
if msp:
|
||||
option = 'Apply ITS Settings'
|
||||
menus['Configure System'].options[option]['Selected'] = True
|
||||
|
||||
# ESET NOD32 AV
|
||||
print('')
|
||||
if msp or ui.ask('Install ESET NOD32 AV?'):
|
||||
option = 'ESET NOD32 AV'
|
||||
if msp or ui.ask(' For VIP?'):
|
||||
option = f'{option} (MSP)'
|
||||
menus['Install Software'].options[option]['Selected'] = True
|
||||
|
||||
# Re-enable Main Menu action if disabled
|
||||
MENU_PRESETS.actions['Main Menu'].update({'Disabled':False, 'Hidden':False})
|
||||
|
|
@ -238,6 +313,9 @@ def run_auto_setup(base_menus, presets) -> None:
|
|||
# Check OS and update title for menus
|
||||
title = check_os_and_set_menu_title(title)
|
||||
|
||||
# Check if AV scan needs to be run
|
||||
check_if_auto_repairs_are_needed()
|
||||
|
||||
# Generate menus
|
||||
menus = build_menus(base_menus, title, presets)
|
||||
|
||||
|
|
@ -371,6 +449,11 @@ def auto_enable_regback() -> None:
|
|||
)
|
||||
|
||||
|
||||
def auto_apply_its_settings():
|
||||
"""Apply ITS settings."""
|
||||
TRY_PRINT.run('Apply ITS settings...', apply_its_settings)
|
||||
|
||||
|
||||
def auto_system_restore_enable() -> None:
|
||||
"""Enable System Restore."""
|
||||
cmd = [
|
||||
|
|
@ -404,7 +487,6 @@ def auto_activate_windows() -> None:
|
|||
def auto_config_browsers() -> None:
|
||||
"""Configure Browsers."""
|
||||
prompt = ' Press Enter to continue...'
|
||||
TRY_PRINT.run('Chrome Notifications...', disable_chrome_notifications)
|
||||
TRY_PRINT.run(
|
||||
'uBlock Origin...', enable_ublock_origin, msg_good='STARTED',
|
||||
)
|
||||
|
|
@ -428,16 +510,49 @@ def auto_config_open_shell() -> None:
|
|||
TRY_PRINT.run('Open Shell...', config_open_shell)
|
||||
|
||||
|
||||
def auto_disable_chrome_notifications():
|
||||
"""Disable Chrome notifications.
|
||||
|
||||
NOTE: This can cause Chrome Sync to be re-authenticated."""
|
||||
TRY_PRINT.run('Disable Chrome Notifications...', disable_chrome_notifications)
|
||||
|
||||
|
||||
def auto_disable_fast_startup():
|
||||
"""Disable fast startup (i.e. Hiberboot)."""
|
||||
TRY_PRINT.run(
|
||||
'Disable Fast Startup...', reg_set_value, 'HKLM',
|
||||
r'SYSTEM\CurrentControlSet\Control\Session Manager\Power',
|
||||
'HiberbootEnabled', 0, 'DWORD',
|
||||
)
|
||||
|
||||
|
||||
def auto_disable_password_expiration() -> None:
|
||||
"""Disable password expiration for all users."""
|
||||
TRY_PRINT.run('Disable password expiration...', disable_password_expiration)
|
||||
|
||||
|
||||
def auto_enable_hibernation():
|
||||
"""Enable Hibernation."""
|
||||
TRY_PRINT.run(
|
||||
'Enable Hibernation...', run_program, ['powercfg', '/hibernate', 'on'],
|
||||
)
|
||||
|
||||
|
||||
def auto_export_aida64_report() -> None:
|
||||
"""Export AIDA64 reports."""
|
||||
TRY_PRINT.run('AIDA64 Report...', export_aida64_report)
|
||||
|
||||
|
||||
def auto_install_eset_nod32_av():
|
||||
"""Install ESET NOD32 AV."""
|
||||
TRY_PRINT.run('ESET NOD32...', install_eset_nod32_av)
|
||||
|
||||
|
||||
def auto_install_eset_nod32_av_msp():
|
||||
"""Install ESET NOD32 AV with MSP settings."""
|
||||
TRY_PRINT.run('ESET NOD32 (MSP)...', install_eset_nod32_av, msp=True)
|
||||
|
||||
|
||||
def auto_install_firefox() -> None:
|
||||
"""Install Firefox."""
|
||||
TRY_PRINT.run('Firefox...', install_firefox)
|
||||
|
|
@ -449,7 +564,10 @@ def auto_install_libreoffice() -> None:
|
|||
NOTE: It is assumed that auto_install_vcredists() will be run
|
||||
before this function to satisfy the vcredist=False usage.
|
||||
"""
|
||||
TRY_PRINT.run('LibreOffice...', install_libreoffice, vcredist=False)
|
||||
TRY_PRINT.run(
|
||||
'LibreOffice...', install_libreoffice,
|
||||
use_mso_formats=True, vcredist=False,
|
||||
)
|
||||
|
||||
|
||||
def auto_install_open_shell() -> None:
|
||||
|
|
@ -482,9 +600,9 @@ def auto_open_device_manager() -> None:
|
|||
TRY_PRINT.run('Device Manager...', open_device_manager)
|
||||
|
||||
|
||||
def auto_open_hwinfo_sensors() -> None:
|
||||
"""Open HWiNFO Sensors."""
|
||||
TRY_PRINT.run('HWiNFO Sensors...', open_hwinfo_sensors)
|
||||
def auto_open_mic_and_webcam_tests():
|
||||
"""Open Webcam Tests."""
|
||||
TRY_PRINT.run('Webcam Tests...', open_mic_and_webcam_tests)
|
||||
|
||||
|
||||
def auto_open_microsoft_store_updates() -> None:
|
||||
|
|
@ -552,6 +670,11 @@ def auto_show_storage_status() -> None:
|
|||
TRY_PRINT.run('Storage Status...', get_storage_status)
|
||||
|
||||
|
||||
def auto_shutup_10():
|
||||
"""Disable Windows telemetry using O&O ShutUp 10."""
|
||||
TRY_PRINT.run('Disable Telemetry...', run_shutup_10)
|
||||
|
||||
|
||||
def auto_windows_temp_fix() -> None:
|
||||
"""Restore default ACLs for Windows\\Temp."""
|
||||
TRY_PRINT.run(r'Windows\Temp fix...', fix_windows_temp)
|
||||
|
|
@ -562,7 +685,12 @@ def config_explorer() -> None:
|
|||
"""Configure Windows Explorer and restart the process."""
|
||||
reg_write_settings(REG_WINDOWS_EXPLORER)
|
||||
kill_procs('explorer.exe', force=True)
|
||||
popen_program(['explorer.exe'])
|
||||
popen_program([fr'{SYSTEMDRIVE}\Windows\explorer.exe'])
|
||||
sleep(3)
|
||||
for _ in range(30):
|
||||
if get_procs('explorer.exe'):
|
||||
break
|
||||
sleep(1)
|
||||
|
||||
|
||||
def config_open_shell() -> None:
|
||||
|
|
@ -681,6 +809,20 @@ def fix_windows_temp() -> None:
|
|||
|
||||
|
||||
# Install Functions
|
||||
def install_eset_nod32_av(msp=False):
|
||||
"""Install ESET NOD32 Antivirus."""
|
||||
config_file = f'eset-config{"-msp" if msp else ""}.xml'
|
||||
reg_write_settings(REG_ESET_NOD32_SETTINGS)
|
||||
run_tool(
|
||||
'ESET', 'ESET_NOD32_AV',
|
||||
'--silent', '--accepteula',
|
||||
'--msi-property', 'PRODUCTTYPE=eav',
|
||||
'PRODUCT_LANG=1033', 'PRODUCT_LANG_CODE=en-US',
|
||||
f'ADMINCFG="{config_file}"',
|
||||
cwd=True, download=True,
|
||||
)
|
||||
|
||||
|
||||
def install_firefox() -> None:
|
||||
"""Install Firefox.
|
||||
|
||||
|
|
@ -837,6 +979,11 @@ def uninstall_firefox() -> None:
|
|||
|
||||
|
||||
# Misc Functions
|
||||
def apply_its_settings():
|
||||
"""Apply ITS settings."""
|
||||
create_custom_power_plan(enable_sleep=False)
|
||||
|
||||
|
||||
def check_secure_boot_status() -> None:
|
||||
"""Check Secure Boot status."""
|
||||
is_secure_boot_enabled(raise_exceptions=True, show_alert=True)
|
||||
|
|
@ -924,20 +1071,11 @@ def open_device_manager() -> None:
|
|||
popen_program(['mmc', 'devmgmt.msc'])
|
||||
|
||||
|
||||
def open_hwinfo_sensors() -> None:
|
||||
"""Open HWiNFO sensors."""
|
||||
hwinfo_path = get_tool_path('HWiNFO', 'HWiNFO')
|
||||
base_config = hwinfo_path.with_name('general.ini')
|
||||
|
||||
# Write new config to disk
|
||||
with open(hwinfo_path.with_suffix('.ini'), 'w', encoding='utf-8') as _f:
|
||||
_f.write(
|
||||
f'{base_config.read_text(encoding="utf-8")}\n'
|
||||
'SensorsOnly=1\nSummaryOnly=0\n'
|
||||
)
|
||||
|
||||
# Open HWiNFO
|
||||
run_tool('HWiNFO', 'HWiNFO', popen=True)
|
||||
def open_mic_and_webcam_tests():
|
||||
"""Open Webcam Tests."""
|
||||
webbrowser.open('https://mictests.com/')
|
||||
sleep(1)
|
||||
webbrowser.open('http://webcamtests.com/')
|
||||
|
||||
|
||||
def open_microsoft_store_updates() -> None:
|
||||
|
|
@ -953,7 +1091,10 @@ def open_snappy_driver_installer_origin() -> None:
|
|||
stop_service(svc)
|
||||
if any([get_service_status(s) != 'stopped' for s in appid_services]):
|
||||
raise GenericWarning('Failed to stop AppID services')
|
||||
run_tool('SDIO', 'SDIO', cwd=True, pipe=True, popen=True)
|
||||
sdio_path = get_sdio_path(interactive=False)
|
||||
log_dir = format_log_path(tool=True).parent
|
||||
cmd = [sdio_path, '-log_dir', log_dir]
|
||||
popen_program(cmd, cwd=sdio_path.parent, pipe=True)
|
||||
|
||||
|
||||
def open_windows_activation() -> None:
|
||||
|
|
@ -969,7 +1110,12 @@ def open_windows_updates() -> None:
|
|||
def open_xmplay() -> None:
|
||||
"""Open XMPlay."""
|
||||
sleep(2)
|
||||
run_tool('XMPlay', 'XMPlay', 'music.7z', cwd=True, popen=True)
|
||||
run_tool('XMPlay', 'XMPlay', 'music.m3u', cwd=True, popen=True)
|
||||
|
||||
|
||||
def run_shutup_10():
|
||||
"""Disable Windows telemetry using O&O ShutUp 10."""
|
||||
run_tool('ShutUp10', 'OOSU10', '1201.cfg', '/quiet', cwd=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ def fix_layout(
|
|||
# Calculate constraints
|
||||
avail_horizontal, avail_vertical = get_window_size()
|
||||
avail_vertical -= layout['Current'].get('height', 0)
|
||||
for group in ('Title', 'Info'):
|
||||
for group in ('Title', 'Subtitle', 'Info'):
|
||||
if not layout[group]['Panes']:
|
||||
continue
|
||||
avail_vertical -= layout[group].get('height', 0) + 1
|
||||
|
|
@ -97,7 +97,7 @@ def fix_layout(
|
|||
)
|
||||
for group, data in layout.items():
|
||||
num_panes = len(data['Panes'])
|
||||
if num_panes < 2 or group not in ('Title', 'Info'):
|
||||
if num_panes < 2 or group not in ('Title', 'Subtitle', 'Info'):
|
||||
continue
|
||||
pane_width, remainder = divmod(avail_horizontal - (num_panes-1), num_panes)
|
||||
for pane_id in data['Panes']:
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ TMUX_SIDE_WIDTH = 21
|
|||
TMUX_TITLE_HEIGHT = 2
|
||||
TMUX_LAYOUT = { # NOTE: This needs to be in order from top to bottom
|
||||
'Title': {'Panes': [], 'height': TMUX_TITLE_HEIGHT},
|
||||
'Subtitle': {'Panes': [], 'height': TMUX_TITLE_HEIGHT},
|
||||
'Info': {'Panes': []},
|
||||
'Current': {'Panes': [environ.get('TMUX_PANE', None)]},
|
||||
'Workers': {'Panes': []},
|
||||
|
|
@ -115,6 +116,29 @@ class TUI():
|
|||
# Add pane
|
||||
self.layout['Title']['Panes'].append(tmux.split_window(**tmux_args))
|
||||
|
||||
def add_subtitle_pane(self, line1: str, line2: str) -> None:
|
||||
"""Add pane to subtitle row."""
|
||||
lines = [line1, line2]
|
||||
tmux_args = {
|
||||
'behind': True,
|
||||
'lines': TMUX_TITLE_HEIGHT,
|
||||
'target_id': None,
|
||||
'text': '\n'.join(lines),
|
||||
'vertical': True,
|
||||
}
|
||||
if self.layout['Subtitle']['Panes']:
|
||||
tmux_args.update({
|
||||
'behind': False,
|
||||
'percent': 50,
|
||||
'target_id': self.layout['Subtitle']['Panes'][-1],
|
||||
'text': '\n'.join(lines),
|
||||
'vertical': False,
|
||||
})
|
||||
tmux_args.pop('lines')
|
||||
|
||||
# Add pane
|
||||
self.layout['Subtitle']['Panes'].append(tmux.split_window(**tmux_args))
|
||||
|
||||
def add_worker_pane(
|
||||
self,
|
||||
lines: int | None = None,
|
||||
|
|
@ -221,6 +245,12 @@ class TUI():
|
|||
self.layout['Info']['Panes'].clear()
|
||||
tmux.kill_pane(*panes)
|
||||
|
||||
def remove_all_subtitle_panes(self) -> None:
|
||||
"""Remove all subtitle panes and update layout."""
|
||||
panes = self.layout['Subtitle']['Panes'].copy()
|
||||
self.layout['Subtitle']['Panes'].clear()
|
||||
tmux.kill_pane(*panes)
|
||||
|
||||
def remove_all_worker_panes(self) -> None:
|
||||
"""Remove all worker panes and update layout."""
|
||||
self.layout['Workers'].pop('height', None)
|
||||
|
|
@ -242,6 +272,13 @@ class TUI():
|
|||
self.layout['Title']['Panes'] = panes[:1]
|
||||
self.set_title(line1, line2, colors)
|
||||
|
||||
def reset_subtitle_pane(self) -> None:
|
||||
"""Remove all extra subtitle panes and update layout."""
|
||||
panes = self.layout['Subtitle']['Panes'].copy()
|
||||
if len(panes) > 1:
|
||||
tmux.kill_pane(*panes[1:])
|
||||
self.layout['Subtitle']['Panes'] = panes[:1]
|
||||
|
||||
def set_current_pane_height(self, height: int) -> None:
|
||||
"""Set current pane height and update layout."""
|
||||
self.layout['Current']['height'] = height
|
||||
|
|
@ -323,7 +360,7 @@ def fix_layout(layout, forced: bool = False) -> None:
|
|||
pass
|
||||
|
||||
# Update "group" panes widths
|
||||
for group in ('Title', 'Info'):
|
||||
for group in ('Title', 'Subtitle', 'Info'):
|
||||
num_panes = len(layout[group]['Panes'])
|
||||
if num_panes <= 1:
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -81,9 +81,10 @@ function copy_live_env() {
|
|||
rsync -aI "$ROOT_DIR/scripts/" "$PROFILE_DIR/airootfs/usr/local/bin/"
|
||||
|
||||
echo "Copying WizardKit UFD files..."
|
||||
rsync -aI "$ROOT_DIR/setup/ufd/" "$PROFILE_DIR/airootfs/usr/share/WizardKit/"
|
||||
rsync -aI --exclude="macOS-boot-icon.tar" "$ROOT_DIR/setup/ufd/" "$PROFILE_DIR/airootfs/usr/share/WizardKit/"
|
||||
tar xaf "$ROOT_DIR/setup/ufd/macOS-boot-icon.tar" -C "$PROFILE_DIR/airootfs/usr/share/WizardKit"
|
||||
cp "$ROOT_DIR/images/rEFInd.png" "$PROFILE_DIR/airootfs/usr/share/WizardKit/EFI/Boot/rEFInd.png"
|
||||
cp "$ROOT_DIR/images/Syslinux.png" "$PROFILE_DIR/airootfs/usr/share/WizardKit/syslinux/syslinux.png"
|
||||
cp "$ROOT_DIR/images/Syslinux.jpg" "$PROFILE_DIR/airootfs/usr/share/WizardKit/syslinux/syslinux.jpg"
|
||||
|
||||
echo "Copying Memtest86+ files..."
|
||||
rsync -aI "/boot/memtest86+/memtest.bin" "$PROFILE_DIR/airootfs/usr/share/WizardKit/syslinux/"
|
||||
|
|
@ -127,8 +128,7 @@ function update_live_env() {
|
|||
label="${KIT_NAME_SHORT}_LINUX"
|
||||
|
||||
# Boot config
|
||||
cp "$ROOT_DIR/images/Syslinux.png" "$PROFILE_DIR/syslinux/splash.png"
|
||||
sed -i -r "s/___+/${KIT_NAME_FULL}/" "$PROFILE_DIR/airootfs/usr/share/WizardKit/syslinux/syslinux.cfg"
|
||||
cp "$ROOT_DIR/images/Syslinux.jpg" "$PROFILE_DIR/syslinux/splash.jpg"
|
||||
|
||||
# MOTD
|
||||
sed -i -r "s/KIT_NAME_SHORT/$KIT_NAME_SHORT/" "$PROFILE_DIR/profiledef.sh"
|
||||
|
|
@ -196,7 +196,7 @@ function update_live_env() {
|
|||
|
||||
# Wallpaper
|
||||
mkdir -p "$PROFILE_DIR/airootfs/usr/share/wallpaper"
|
||||
cp "$ROOT_DIR/images/Linux.png" "$PROFILE_DIR/airootfs/usr/share/wallpaper/burned.in"
|
||||
cp "$ROOT_DIR/images/Linux.jpg" "$PROFILE_DIR/airootfs/usr/share/wallpaper/burned.in"
|
||||
|
||||
# udevil
|
||||
mkdir -p "$PROFILE_DIR/airootfs/media"
|
||||
|
|
|
|||
|
|
@ -195,10 +195,10 @@ ln -s /Volumes/RAM_Disk/Logs "${WK_PATH}/var/root/Logs"
|
|||
cp -a "${LINUX_DIR}/profile_base/airootfs/etc/skel/.tmux.conf" "${WK_PATH}/etc/tmux.conf"
|
||||
rsync -aS /usr/bin/{env,killall} "${WK_PATH}/usr/bin"/
|
||||
rsync -aS "${MACOS_DIR}/live-macos-startup" "${ROOT_DIR}/scripts/" "${WK_PATH}/usr/local/bin"/
|
||||
if [[ -f "${ROOT_DIR}/images/macOS-${OS_VERSION:0:5}.png" ]]; then
|
||||
cp -a "${ROOT_DIR}/images/macOS-${OS_VERSION:0:5}.png" "${WK_PATH}/usr/local/wallpaper.png"
|
||||
if [[ -f "${ROOT_DIR}/images/macOS-${OS_VERSION:0:5}.jpg" ]]; then
|
||||
cp -a "${ROOT_DIR}/images/macOS-${OS_VERSION:0:5}.jpg" "${WK_PATH}/usr/local/wallpaper.jpg"
|
||||
else
|
||||
cp -a "${ROOT_DIR}/images/macOS.png" "${WK_PATH}/usr/local/wallpaper.png"
|
||||
cp -a "${ROOT_DIR}/images/macOS.jpg" "${WK_PATH}/usr/local/wallpaper.jpg"
|
||||
fi
|
||||
|
||||
# Unbless
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@
|
|||
setlocal EnableDelayedExpansion
|
||||
title WizardKit: Build Tool
|
||||
call :CheckFlags %*
|
||||
rem TODO: Remove warning
|
||||
echo "Windows PE build is currently under development"
|
||||
echo " Proceeding will likely result in errors so be warned"
|
||||
pause
|
||||
call :CheckElevation || goto Exit
|
||||
call :FindKitsRoot || goto ErrorKitNotFound
|
||||
|
||||
|
|
@ -19,14 +15,8 @@ set "dandi_set_env=%adk_root%\Deployment Tools\DandISetEnv.bat"
|
|||
if not exist "%dandi_set_env%" (goto ErrorKitNotFound)
|
||||
call "%dandi_set_env%" || goto ErrorUnknown
|
||||
|
||||
:EnsureCRLF
|
||||
rem Rewrite main.py using PowerShell to have CRLF/`r`n lineendings
|
||||
set "script=%~dp0\.bin\Scripts\borrowed\set-eol.ps1"
|
||||
set "main=%~dp0\.bin\Scripts\settings\main.py"
|
||||
powershell -executionpolicy bypass -noprofile -file %script% -lineEnding win -file %main% || goto ErrorUnknown
|
||||
|
||||
:Launch
|
||||
set "script=%~dp0\.bin\Scripts\build_pe.ps1"
|
||||
set "script=%~dp0\pe\build_pe.ps1"
|
||||
powershell -executionpolicy bypass -noprofile -file %script% || goto ErrorUnknown
|
||||
goto Exit
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ rem robocopy /e windows/cbin %OUT_DIR%\.cbin
|
|||
mkdir %OUT_DIR%\.cbin
|
||||
copy ..\LICENSE.txt %OUT_DIR%\LICENSE.txt
|
||||
copy ..\README.md %OUT_DIR%\README.md
|
||||
copy ..\images\ConEmu.png %OUT_DIR%\.bin\ConEmu\
|
||||
copy ..\images\ConEmu.jpg %OUT_DIR%\.bin\ConEmu\
|
||||
attrib +h %OUT_DIR%\.bin >nul 2>&1
|
||||
attrib +h %OUT_DIR%\.cbin >nul 2>&1
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,7 @@
|
|||
#Put SSH keys here
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAgEAhF5u/wn04dSDI1mg6HlbDhmM3orJtu2kZdOTBd+/kvCxdC4h+AuwRSWjbIhGi9s8R3iq64gC6TJ2dX4skJ5fMeYqHMZieGgfXderRiPgMURCDWONLZCWyowBun5qGaXkYQo5VbLUNGXua9+RMXaxoJSCOhxq/6CzdleD6pkNNgL5a1ZarD3wLCNRsS50OmyGytXsYTClAY+F9lTd4VXjKuZpjqGfhA5Xz09Rad3Me2Tsd07cdO3LxDNlr969Q/hEwfZ/g+ePaIO0Z/zFHIO4J6Mnt8POTU1fM99tgqUQHWZRP2A+9OYx7nApdA4IFWVUhNfsVkrgcgYdCLD8U16Cdfwm1i1RiBhlYBfzA+EDjD6cbegaaMCKhsCZMZKQB4LyJTDgjvCrk1Q7dE0Nc1Nq8qD/BMbZkIKQcQH8xrX1hONayUMdOPnpDpi6IP2NIYFTbdJD70JQ8ru+gIDRQJR6g0AwnjMoJYNRKgAtWlcbKQ8YQW/FNREtyUhh8tquCyFbofGUiJxmTbWki7u7VJxRLNSnp+NQhPNT4FUbWy1vPrJi0l3MxuXdG3nZ0Brnggn72tnGcAUOmRTPubNlkhFStqcCM+tAVTHeLwHobdMewKQMWGPt6UXLtEJINqW9nCh1SAOA3cjsZ3ugXLRQQc82rEgTfTp2RtuN+NSf/vgoDbk= 1201 PuTTY Key
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGZqPA7/T3zw8YipngM6vKpxX+IMpwqm2jad+NcJWxC0Hh09oVUalgbO/eWRloQlNgxHTosdIkSp++xnyeEHI3vL0pbko/c0OJbs6vEsHp8ljzt61HKKsscVADCI/ogGwHkPIHENgbiXkBPutEBLYFPtMcJQw6GBIkagW+dTyqEw/8c1cDmTq7AM74tHqgrpYjJy35Nne0K+zGIeqzTJdtg3fkRCN9JH24c3VbYoeIpMlgfxxgRG44DC/o0BRoMr3wWiS/sTrutlluB03vaQWe+4o8p789wq+fDqKhBTisH96RQ1Y1D5eZcf0DngBJtnCLWPG+z6YP0DFW5DYeQRoYNneRkR10RUqN0Eufok/UMNnxWcW+TztTxO5JSucXt0MYpLsWO1U7B22MR1W7oVlbznUrVENSQGRqyOS4PdqmI4faF0vEKis68S7NaXg88zwBr612HwFBianXvvxhkyEKv8ECbeXSvHcIajepdzbGLwaussDQiNv8vyX/8o3K7Si/ktdgIK4UzfaMq7G0vX1KEvuKAfMPYn4z0zuc/ahxTTfvHqfO+cnYgH8Pnzi1DGziRveeKIFT4OKOP5C3wHntsfv1CStK/LXhH3j4PNRuet0Fm5+jUJ22DQqlXtFiwFz4E+Bt+67exhm4Out4rAUqCzjDW5+mw0WU2P7u01cT+Q== admin@anaconda.1201.com
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDaJkYcxi7eSpRE7i0n2N2q4CHebPqxYc5eLQ2mDbrLpAIRUSdVjqe8RiVLJ5LKejkGMnWbaiDZ59cLWJhDTfqZFCRexVQkA9v8MVHUWINzxqfJFOnz7dKXP0PmggDbuKMIUkY65KtOLC2Cnqs3epmysQxrSf33W3IqPwL2XjJQXRbFLd4UG1bSZtBAAF3C/i7hBzb2iQPIpr1v1rCrW6gVhrgyoN7ZTfMqcKHnTPsUn95Qtyp6NUYKh4ctrPmTcTHFfwmgQiAYQ6jMcH8bNBf1XDmJuEIwaILYVzzrWOXlx84rqaEUdeRbvd4S/G9FcRt9eO3o+jD4hupdyvIDCttP3ct6yeWQVotreZE/5OVwfxr0BNXPBmTiyF2c6cRrMlXpQMoRQyjnD5Eua0VEUJzj2ows5APGmciJudDw4rXoRLOZRX/nDuXqgNJJ6q+MgRx3hor/etbV2mXcmSMdoI8DuybFzHuEb9GRwYhLxzDdDe+zmevx0rl01W8EJImqgrTwMblADbM4VHpC974d5w2s9uoXJg1NY4UVkSjJdK0De0kyGh8hK2pqTqCBh1OznB43Yla5PwM+05JIMySVRuivvZ1+tBh8ZURzIbFFIrdgNiZIooI4hA/m8qz4CTWIxKWstXR08eOufIbjV+R6VJjEssvLmgYw93rUJ4WbQNbaiw== twoshirt@anaconda.1201.com
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDKjMOQl/re3sTBUFeNEYOt2DsZLEeQiFfzu1tFlMUGcLnpjpA/Zhp0qpBBVhFS9j8p7s1838v+HS5dN1yLhWjjdM7EbzIwczJTe83D52GPwOisVWyL6cdB++bynl6LQRGe/dZu19uO1nX3sVU8gime4KLMasihCWw4XATQC1fFB6CnTu62ps3jO1n1MnjsX4odjC/Und5xD39trGU5aO4l17Zns+OBCLwsERofvkb02XZaqmcAXie+/o3jS43dANJXhtsqk0dRiNONccsnshFP90ujR8mPQU1h15fppwI/mRJ4GwNkA7XMcHghj/KqOIptSf0w8bBBQ7RTJATG0nPw45BJHu1bHZEbECpHBbM6TIIKgYVm7cJs54kf0yN66UWzDOfzqjL8Mo/Gmu/bHKhCg+gEQg4xFKbBtB8adMCrj8q9JZ2x8a9l9wcUKpSN8DM4SWUP4F4uXe0cAHhrMIAOfCFcX1lBsilNxidE5fZUGW3WJJ3+WUkeWnfifEBDZpSV0BCWjHKz1R/tQeJfBS8sjjGSheIJZBxLYc+6IUdL77Zjy2byzQ7m4PfaMDI3CxXhkhEeC4AnCn6/quRLFuChrvRYLC9UDrrmMMB4xo4SGsWKnM83orD1UbtbsqfJaHLBGF3WgII77uDvmUnwZJmaHCYM4N97lkoqNgC4VAEd4Q== twoshirt@dinraal.2shirt.work
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDa9lUItn9ooNu2FrOUMHytoQlAtH0x+tcszmt0ZNZN6DU6H87bFtZcB3gaanU4Kr3mIaJBUSjgbafJHiiYXvbzt+Gx7iQxAsXQkOfzArKHu9WV5OIHBATtONI3pokF3crlHJVR7hjY1lvP07c+F/Oygt0Hjd2RcslxPjqSVBRTl6z8Yu7r/BbkNiZRJfuJW/hNg+tGYRFHM6uy0qZ3fOwUmQ+MqCXm3V9K0A/e8dNyHEkW2v4CuAW8QP7gjA4nT/jrkJAA98firwSJdMgpZIFSuzf48D/AhDGuE55W5m/ItpMjVO7oDvqrEygyMO1ugxrIVqj7EM3HtArOtsMDueyCcTd5tWs/e80Y7/E4iB4bQQGo9ifA9oPqywAfETqvO0VUtjO9cMt7/Xnhc9qLxwKstr9UX9ORFm8hiaq9jVPkbwpmBIdfBpk4YwbY/1ObRQmOgsIuq5otPMrw17ymrJCagxk74BAOiMoUte5fhBezMl0ZlsBA9yWw63PHchWCyXQ0o195A3e04Api2FYx2er3mA28vhj8WkXwsuF1C2nypztn0GlmVoDKK5Z//slefLWAYq+V7AyGW3x2zYi4WpBiyM+FmG4ABVw5alxJjkvsoC1Ho7kKjQAdARhy+HsveSYLPPaSsnFIexpTjPlNlSquZG2rXxWLquo40CMMbRdJbw== twoshirt@farosh
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCh+EGFqMfJmfOQvfqfv/ilDL80krdZNdBvJ8xFWYMRy8z4z1Ih7BlCfkGObyxKLIYJgkPPhN3xLWWpN78ME7YeeUF1Ly84oIJABvK9CuZPDcUwDhpLoBli/qxoF/YyLufqtECKaQpDWt+iB1522XcVUerRFMGX3brE+b49gmXSuCC0lPijqcWV9KZVNKj9EStgY5b4DTjINOoiHIHBFsov37wt/+qJn7th+EDqcPndIcTQ31VbcGqEWtfNQWPGVmH898Q2XLvCbN+seBK9ZmttcXzWlS70qUr4xHrZCnegwMnBKPQ0sVo7NtC5DQHrN4I4NyvY0fr/o16nIphfaNZgy/CDo1WauBjJlbQ9MRcFVSPhL3LeyiBtBwC6Nr/qR+6Xjf/BV0ybw8MjXukZxKfW9xJsL631Xrk1/oYJnNKL5IyhIsVFRtfwcB4YfPEZbrdBxrse0Ypchgvpl5srTQfEL5ZTf/ozWCUST2Bf+dDMYrdqudg5zhPU6prneaEzlBxiLqGwBId9bzIqqHD+fE4a90oOX/WpZW0PFApnXGkPahaTPHGOAMfM+DmXdr2e5emdRZp+wgZU1EXwUzKVwE4fnj6HhuUlAt18LBZ/WLHMDGryuKemW0z9X9CUhzgkAmzgRCJ09ZXLYcwuyLwv9HnMQUa6RlWK69s3T/m+Y8fzKQ== twoshirt@mipha.2shirt.work
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC7MHLxoozeHOmFpjIGaXi8m0LuHZb4eUjX6x50XUlP81oVHbl4iAqw/N4r4j+Lg+lga7xcTeBMNWsa/tGxYkAfF8TP8M0S8XEMOR04r9iA4V6wQ8nGhv99y24NZMJPfiseNjDEwoPYPP5+zpCNfvxeMXxz3EhLFwzSp1dz+f1tBcDd8OtxDAGPn1cIM4Ms9gmIv3/HxOinW4IHxEbsZUWtKGPumsuIYwObfBYZPK0WtK23LPYqtReLYbzQmsf5SPpJAx20mwtZm8Y+LBXAKuxWEKzlF4l/ubqjTfMff5t+8FPEyCgRwm8VcMqxA1b04Pkcuq08AuooJuLKHOJ+aSyDWGXrSf3RlUAx8GafYOpZrcMEhQ0Q1a0XqZNTUXrf13qVPBqnKV8asOXrYIP56Z3zp8qoHasT8t5PbYrwvtV7weW/X61cQf/nn1B/BJbJr/rrOOxdqRCsH44OlElrhjwHsuzQbMc4U4wwoazlMmfNKsKUaoBmLPDTfmMBwnCuBrngNY9JsSB778fqwxsyLSfDTcIDW2kGuQ4P/4u5cKNw/q8Zo15Ghw2327kyTK9e4EGPCpBk+s5832FWS0uJLV269R5aZ5dYzYU13djfVOw2ypZTdr55nTlIH/31iSmJS0PBKyRLa2a7YxErt37W3f2U1eUt+j91LeaP5cMKHkWAsw== twoshirt@naydra.2shirt.work
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# Put known WiFi networks in the form:
|
||||
#SSID-5G:::Password
|
||||
#SSID-2G:::hunter2
|
||||
1201computersXI:::wifipassword
|
||||
1201computersXI-5g:::wifipassword
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
aic94xx-firmware
|
||||
ddrescueview-bin
|
||||
hardinfo-gtk3
|
||||
hfsprogs
|
||||
iwgtk
|
||||
memtest86-efi
|
||||
mprime-bin
|
||||
opensuperclone-git
|
||||
pipes.sh
|
||||
python-mariadb-connector
|
||||
python-smbus2
|
||||
smartmontools-svn
|
||||
ttf-font-awesome-4
|
||||
udevil
|
||||
wd719x-firmware
|
||||
wimboot-bin
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
aic94xx-firmware
|
||||
adobe-source-han-sans-otc-fonts
|
||||
adobe-source-sans-fonts
|
||||
alsa-utils
|
||||
amd-ucode
|
||||
antiword
|
||||
|
|
@ -8,12 +9,9 @@ arch-install-scripts
|
|||
base
|
||||
bc
|
||||
bind
|
||||
bluez
|
||||
bluez-utils
|
||||
bolt
|
||||
btrfs-progs
|
||||
cbatticon
|
||||
chntpw
|
||||
cmatrix
|
||||
colordiff
|
||||
conky
|
||||
|
|
@ -25,6 +23,7 @@ ddrescueview-bin
|
|||
device-mapper
|
||||
diffutils
|
||||
dkms
|
||||
dmde
|
||||
dmidecode
|
||||
dmraid
|
||||
dos2unix
|
||||
|
|
@ -39,16 +38,12 @@ f2fs-tools
|
|||
fatresize
|
||||
feh
|
||||
ffmpeg
|
||||
firefox
|
||||
foot-terminfo
|
||||
gnome-keyring
|
||||
gnu-netcat
|
||||
gparted
|
||||
gpicview
|
||||
gptfdisk
|
||||
grub
|
||||
gsmartcontrol
|
||||
hardinfo-gtk3
|
||||
hexedit
|
||||
hfsprogs
|
||||
htop
|
||||
|
|
@ -70,12 +65,14 @@ libxft
|
|||
linux
|
||||
linux-firmware
|
||||
linux-firmware-marvell
|
||||
linux-headers
|
||||
lm_sensors
|
||||
lsscsi
|
||||
lvm2
|
||||
lzip
|
||||
man-db
|
||||
man-pages
|
||||
mariadb-clients
|
||||
mdadm
|
||||
mediainfo
|
||||
memtest86-efi
|
||||
|
|
@ -92,32 +89,35 @@ nano
|
|||
nbd
|
||||
ncdu
|
||||
ndisc6
|
||||
netsurf
|
||||
nfs-utils
|
||||
nmap
|
||||
noto-fonts
|
||||
noto-fonts-cjk
|
||||
ntfs-3g
|
||||
numlockx
|
||||
nvme-cli
|
||||
open-iscsi
|
||||
openbox
|
||||
openssh
|
||||
opensuperclone-git
|
||||
otf-font-awesome-4
|
||||
p7zip
|
||||
papirus-icon-theme
|
||||
parted
|
||||
pcmanfm
|
||||
perl
|
||||
picom
|
||||
pipes.sh
|
||||
pv
|
||||
python
|
||||
python-docopt
|
||||
python-mariadb-connector
|
||||
python-matplotlib
|
||||
python-packaging
|
||||
python-prompt_toolkit
|
||||
python-psutil
|
||||
python-pytz
|
||||
python-requests
|
||||
qemu-guest-agent
|
||||
ranger
|
||||
refind
|
||||
reiserfsprogs
|
||||
rfkill
|
||||
|
|
@ -127,10 +127,10 @@ rxvt-unicode
|
|||
rxvt-unicode-terminfo
|
||||
sdparm
|
||||
smartmontools-svn
|
||||
smbclient
|
||||
sof-firmware
|
||||
speedtest-cli
|
||||
spice-vdagent
|
||||
squashfs-tools
|
||||
st
|
||||
sudo
|
||||
sysbench
|
||||
|
|
@ -141,7 +141,6 @@ systemd-sysvcompat
|
|||
terminus-font
|
||||
testdisk
|
||||
texinfo
|
||||
thunar
|
||||
tigervnc
|
||||
tint2
|
||||
tk
|
||||
|
|
@ -162,12 +161,9 @@ usb_modeswitch
|
|||
usbmuxd
|
||||
usbutils
|
||||
util-linux
|
||||
veracrypt
|
||||
vim
|
||||
virtualbox-guest-utils
|
||||
volumeicon
|
||||
wd719x-firmware
|
||||
wezterm-terminfo
|
||||
which
|
||||
wimboot-bin
|
||||
wimlib
|
||||
|
|
@ -176,7 +172,6 @@ xarchiver
|
|||
xf86-input-libinput
|
||||
xf86-video-amdgpu
|
||||
xf86-video-fbdev
|
||||
xf86-video-nouveau
|
||||
xf86-video-qxl
|
||||
xf86-video-vesa
|
||||
xfsprogs
|
||||
|
|
|
|||
27
setup/linux/packages/custom/dmde/PKGBUILD
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
pkgname=dmde
|
||||
pkgver=4.0.6.806
|
||||
pkgrel=1
|
||||
pkgdesc="DMDE is a powerful tool for data searching, editing, and recovery on disks"
|
||||
arch=('x86_64')
|
||||
url="https://dmde.com/"
|
||||
license=('custom')
|
||||
makedepends=(unzip)
|
||||
replaces=($pkgname)
|
||||
|
||||
source=("dmde-mine.ini")
|
||||
source_x86_64=("https://dmde.com/download/dmde-4-0-6-806-lin64-gui.zip")
|
||||
|
||||
sha512sums=('1f11bee45672f507c4401031811273e8d6b103358bd0b2bec1cd6ecca1782ec23c0af3f81c23c5fcedd4380169fdea0eab60909ce59e17053029608ba324586a')
|
||||
sha512sums_x86_64=('b2d20d13ced8780baa46a435c772af16b7b16b38c7daf65d2edc55f3560bb2ab417390ffd55cea6f424d4237ff911baa5547a45a9c9e279689fb1fd8fb82fc99')
|
||||
|
||||
package() {
|
||||
cd $srcdir
|
||||
mkdir -p ./usr/share/dmde/ $pkgdir/usr/bin/ $pkgdir/usr/share/applications/ $pkgdir/usr/share/pixmaps/
|
||||
unzip ./dmde-4-0-6-806-lin64-gui.zip -d $srcdir/usr/share/dmde/
|
||||
cp $srcdir/dmde-mine.ini $srcdir/usr/share/dmde/dmde.ini
|
||||
cp -r usr $pkgdir
|
||||
chmod +x $pkgdir/usr/share/dmde/dmde
|
||||
chmod +x $pkgdir/usr/share/dmde/dmde-su
|
||||
ln -sr /usr/share/dmde/dmde $pkgdir/usr/bin/dmde
|
||||
ln -sr /usr/share/dmde/dmde-su $pkgdir/usr/bin/dmde-su
|
||||
}
|
||||
482
setup/linux/packages/custom/dmde/dmde-mine.ini
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
;
|
||||
;dmde.ini - DMDE for Linux Initialization File
|
||||
;
|
||||
|
||||
setupsign=5e440968
|
||||
lickey=
|
||||
activatekey[0]=
|
||||
portablekey=
|
||||
activatedev=
|
||||
|
||||
lngfile=\en
|
||||
;interface language file
|
||||
|
||||
loadlngfiles=%appres%/locals/*.lng
|
||||
;load language files
|
||||
|
||||
loadcptables=%appres%/locals/*.tbl
|
||||
;load code page tables for Unicode-Char,Translit,Upcase conversions
|
||||
|
||||
editortemplates=%appres%/template.txt
|
||||
;editor template file(s) - wildcards are possible
|
||||
|
||||
[CONSOLE]
|
||||
autosizemaxy=
|
||||
autosizemaxx=
|
||||
|
||||
[GUI]
|
||||
mainwndpos=
|
||||
;x,y,cx,cy
|
||||
|
||||
dialogfontsize=
|
||||
;dialogfont=
|
||||
|
||||
;scale percents:
|
||||
dialogwidth=100
|
||||
dialogheight=100
|
||||
|
||||
;editorfont=
|
||||
editorfontsize=
|
||||
;editorfontbold=0
|
||||
|
||||
;previewfiles=*.bmp;*.gif;*.ico;*.jfif;*.jpe;*.jpeg;*.jpg;*.png;*.tif;*.tiff
|
||||
|
||||
[COMMON]
|
||||
mainwndsplit=-1
|
||||
|
||||
;usecodepage=1252
|
||||
;manually select used code page (CP)
|
||||
|
||||
;oemcodepage=850
|
||||
;manually select source OEM CP for DOS names
|
||||
|
||||
applylngcodepage=1
|
||||
;=1: use codepages from the lng file
|
||||
|
||||
translitenable=1
|
||||
;=1: transliterate symbols outside current code page
|
||||
|
||||
viewtranslit=0
|
||||
;=1: transliterate symbols on the screen
|
||||
|
||||
rtlapply=0
|
||||
;=1: use rtl ui for right-to-left languages
|
||||
|
||||
rtldismiss=0
|
||||
;=1: turn off rtl string mirroring if terminal already supports it
|
||||
|
||||
shellopen=xdg-open
|
||||
|
||||
stephints=0
|
||||
;=1: activate step-by-step hints
|
||||
;=0: deactivate step-by-step hints
|
||||
|
||||
popuphints=
|
||||
;=1: activate popup hints (mouse over)
|
||||
;=0: deactivate popup hints (mouse over)
|
||||
|
||||
kilobyte=1000
|
||||
;kilobyte=1024
|
||||
|
||||
displayCHS=0
|
||||
|
||||
editorhexcolsnum=16
|
||||
;editorhexcolsnum=N - maximum columns number for hex editor
|
||||
;editorhexcolsnum=0: auto
|
||||
|
||||
editorhexgroupsize=8
|
||||
|
||||
startup=p
|
||||
;startup=p - open physical drives at startup
|
||||
|
||||
;startupimage=filename - open disk image
|
||||
;startupdev=devicestring - open device at startup
|
||||
;startupraid=raid.ini - load RAID from "raid.ini" at startup
|
||||
;startupcopy=copy.ini - load "Copy sectors" from "copy.ini" at startup
|
||||
|
||||
readonly=0
|
||||
;=1 - globally disable writing to devices
|
||||
devopen_editmode=0
|
||||
;=1 - default edit mode when open using dialog box
|
||||
devload_editmode=0
|
||||
;=1 - default edit mode when load from log-file
|
||||
devapply_ioparamsopt=0
|
||||
;=1 - option to change i/o params when applying changes
|
||||
devapply_editmodeopt=0
|
||||
;=1 - option to set edit mode when applying changes
|
||||
partapply_ioparamsopt=1
|
||||
;=1 - option to change i/o params when applying partitioning changes
|
||||
partapply_editmodeopt=1
|
||||
;=1 - option to set edit mode when applying partitioning changes
|
||||
|
||||
queryclosedevice=1
|
||||
;query when closing device
|
||||
|
||||
showpartitions=1
|
||||
;show partitions when open device
|
||||
|
||||
;editmode=1
|
||||
;enable disk editor edit mode by default
|
||||
|
||||
;Redefine default device enumeration:
|
||||
|
||||
;Linux device enumeration by FindFile:
|
||||
;enumdevs0=0,"/dev/fd*",0,"",""
|
||||
;enumdevs1=0,"/dev/sd?",0,"",""
|
||||
;enumdevs2=0,"/dev/hd?",0,"",""
|
||||
;enumdevs3=0,"/dev/ub?",0,"",""
|
||||
;enumdevs4=0,"/dev/md*",0,"",""
|
||||
;enumdevs5=0,"/dev/scd*",0,"",""
|
||||
|
||||
;include /dev/disks/by-id & /dev/disks/path enumeration:
|
||||
;enumdevs_byid=1
|
||||
|
||||
;devusbdetectex=
|
||||
|
||||
ckeventtick=1
|
||||
;ckeventtick=0 check event every time (slow down operations)
|
||||
;ckeventtick=1 check event every 55ms only
|
||||
|
||||
mousehandler=1
|
||||
;mousehandler=0 - no mouse
|
||||
;mousehandler=1 - use mouse
|
||||
|
||||
inet=2
|
||||
;1:read hosts file and access DNS servers
|
||||
;2:use getaddrinfo function for internet connections
|
||||
|
||||
[IO]
|
||||
;deviopopupontry=3
|
||||
;additional tries num. before displaying i/o dialog box when errors are auto ignored
|
||||
|
||||
deviopopupdelay=2000
|
||||
;additional ms delay before displaying i/o dialog box when errors are auto ignored
|
||||
|
||||
queryioerrors=1
|
||||
querydrivenotready=1
|
||||
|
||||
retries=1
|
||||
;retries=N
|
||||
; read/write retries number for sector with errors (N=0..999)
|
||||
; if N=0 read block only once (the rest of the block after error is zeroed)
|
||||
|
||||
seekretries=1
|
||||
;seekretries=N - read/write auto retries number if sector not found (N=0..64)
|
||||
|
||||
deviojump=0
|
||||
;deviojump=N
|
||||
;jump over N sectors after I/O error
|
||||
|
||||
deviojumpreturn=0
|
||||
;deviojumpreturn=1 - reverse read after jump
|
||||
;deviojumpreturn=0 - do not reverse read
|
||||
|
||||
devioskipfiller=0x50494B53
|
||||
;fill skipped sectors with hex values
|
||||
|
||||
deviobadfiller=0x20444142
|
||||
;deviobadfiller=0x20444142 - fill bad sectors with hex values
|
||||
;deviobadfiller= - do not fill bads
|
||||
|
||||
buffer=131072
|
||||
;buffer=N - disk data transfer block size (N=4096..1048576)
|
||||
|
||||
dblbuffer=2097152
|
||||
;dblbuffer=N -
|
||||
;(N=4096..16777216)
|
||||
|
||||
diskcache=16777216
|
||||
;diskcache=N - disk cache size
|
||||
;(N=4096..33554432)
|
||||
|
||||
;diskmaxmodsize=209715200
|
||||
;max size for pending modifications
|
||||
|
||||
devreset_chkserial=1
|
||||
;check device serial number when resetting handle
|
||||
|
||||
devreset_trynewdevs=1
|
||||
;devreset_trynewdevs=0 ;never try different device names
|
||||
;devreset_trynewdevs=1 ;try different names when needed and serial can be checked
|
||||
;devreset_trynewdevs=2 ;always try different names (not recommended)
|
||||
|
||||
devlistresetonerror=0
|
||||
;devlistresetonerror=1 ;Update the list of devices on a device i/o error
|
||||
|
||||
vhd_writeenabled=0
|
||||
;vhd_writeenabled=1 ;enable partial write support for virtual disk image files (vhd/vmdk/etc.)
|
||||
|
||||
[DR]
|
||||
maxrecoverdepth=1024
|
||||
maxfilerenames=16
|
||||
maxdirrenames=16
|
||||
|
||||
splitfilesize=0
|
||||
|
||||
[FullScan]
|
||||
showvolumesnum=2048
|
||||
|
||||
[FAT]
|
||||
fatdirentryaccept=1
|
||||
;FAT directory entry validation level:
|
||||
;fatdirentryaccept=0 - accept reliable entries within reliable sectors only
|
||||
;fatdirentryaccept=1 - accept any entries within reliable sectors
|
||||
;fatdirentryaccept=2 - accept reliable entries within any sector
|
||||
;fatdirentryaccept=3 - accept any entry within any sector
|
||||
|
||||
fatinvdirtotree=0
|
||||
;fatinvdirtotree=0 - add FAT invalid directories to file panel only
|
||||
;fatinvdirtotree=1 - insert FAT invalid directories into tree also
|
||||
|
||||
fatfoundtoroot=1
|
||||
;fatfoundtoroot=0 - add found root subdirs to a common directory list
|
||||
;fatfoundtoroot=1 - insert found root subdirectories into $Root
|
||||
|
||||
|
||||
volumeseachblock=4194304
|
||||
;blocksize for FAT/NTFS volume search
|
||||
|
||||
fatmaxvolumes=1024
|
||||
|
||||
[NTFSSearch]
|
||||
ntfsmaxvolumes=1024
|
||||
ntfsmaxprocstarts=1024
|
||||
ntfsmaxmftruns=0xffffff
|
||||
ntfsmaxprocmftruns=32768
|
||||
ntfsmaxindxrecs=0
|
||||
;ntfsthoroughsearch=0 - may skip small inserted MFT fragments
|
||||
;ntfsthoroughsearch=1 - more thorough NTFS Search
|
||||
ntfsthoroughsearch=1
|
||||
;ntfsmftshift=0 - process sector aligned MFT records only
|
||||
;ntfsmftshift=N - process MFT shifted by multiple of N bytes (shifted, traced, etc.)
|
||||
ntfsmftshift=1
|
||||
;ntfsrestrunlen=N - small MFT runs (N or less records) to be partially processed only
|
||||
ntfsrestrunlen=4
|
||||
|
||||
[IFACE]
|
||||
filecachesize=32768
|
||||
;max. number of cached file panel items
|
||||
|
||||
maxtreechilditems=2048
|
||||
;max child items number displayed in tree (GUI)
|
||||
|
||||
charclustermap=]xR./=><<!x|[
|
||||
|
||||
;console UI:
|
||||
dlgframe=1
|
||||
dlglistframe=0
|
||||
|
||||
;console chars:
|
||||
charraidbtns=+,30,x,^,v,*
|
||||
|
||||
charmarks=<>x*\u25BC\x20\x20
|
||||
charbtnshadow=\x20\x20\x20
|
||||
charwframe=\u250C\u2500\u2510\u2502\u2502\u2514\u2500\u2518
|
||||
charframe=\u250C\u2500\u2510\u2502\u2502\u2514\u2500\u2518
|
||||
charvscroll=\u25B2\u25BC\u2592\u25A0
|
||||
charhscroll=<>\u2592\u25A0
|
||||
chartree=\u251C\u2514
|
||||
charprogress=\u2588\u2592
|
||||
|
||||
; remove "#" to use color scheme
|
||||
editorcolors#=\ ;color scheme
|
||||
1F,\ ;0 Default
|
||||
1B,\ ;1 Caption
|
||||
0F,\ ;2 Focused
|
||||
70,\ ;3 Selected block
|
||||
0F,\ ;4 Selected object
|
||||
0E,\ ;5 Modified
|
||||
1A,\ ;6 Read Error
|
||||
1A,\ ;7 Title
|
||||
1C,\ ;8 Invalid Value
|
||||
03,\ ;9 selected input
|
||||
07,\ ;10 grayed
|
||||
1B,\ ;11 selected not focused
|
||||
1A,\ ;12 Caption Raid Disk #1
|
||||
17,\ ;13 Caption Raid Disk #2
|
||||
1C ;14 Invalid Input
|
||||
|
||||
editorcolors#=\ ;gray scheme
|
||||
8F,\ ;0 Default
|
||||
8B,\ ;1 Caption
|
||||
0F,\ ;2 Focused
|
||||
70,\ ;3 Selected block
|
||||
0F,\ ;4 Selected object
|
||||
0E,\ ;5 Modified
|
||||
8A,\ ;6 Read Error
|
||||
8A,\ ;7 Title
|
||||
8C,\ ;8 Invalid Value
|
||||
03,\ ;9 selected input
|
||||
07,\ ;10 grayed
|
||||
8B,\ ;11 selected not focused
|
||||
81,\ ;12 Caption Raid Disk #1
|
||||
82,\ ;13 Caption Raid Disk #2
|
||||
8C ;14 Invalid Input
|
||||
|
||||
editorcolors#=\ ;blue scheme
|
||||
1B,\ ;0 Default
|
||||
1E,\ ;1 Caption
|
||||
3F,\ ;2 Focused
|
||||
30,\ ;3 Selected block
|
||||
0F,\ ;4 Selected object
|
||||
0E,\ ;5 Modified
|
||||
1A,\ ;6 Read Error
|
||||
1A,\ ;7 Title
|
||||
1C,\ ;8 Invalid Value
|
||||
3F,\ ;9 selected input
|
||||
07,\ ;10 grayed
|
||||
1F,\ ;11 selected not focused
|
||||
1A,\ ;12 Caption Raid Disk #1
|
||||
17,\ ;13 Caption Raid Disk #2
|
||||
1C ;14 Invalid Input
|
||||
|
||||
;console text attributes:
|
||||
; xxxx:
|
||||
; x... - text background color for shortcut
|
||||
; .x.. - text color for shortcut
|
||||
; ..x. - text background color
|
||||
; ...x - text color
|
||||
; 0=Black 1=Blue 2=Green 3=Cyan 4=Red 5=Magenta 6=Brown 7=ltGray
|
||||
; 8=dkGray 9=ltBlue A=ltGreen B=ltCyan C=ltRed D=ltMagenta E=Yellow F=White
|
||||
|
||||
; remove "#" to use color scheme
|
||||
colors#=\ ;color scheme
|
||||
3E30,\ ;menubox
|
||||
3E30,\ ;menubar
|
||||
0E0E,\ ;highlighted menu item
|
||||
3838,\ ;disabled menu item
|
||||
2E20,\ ;button
|
||||
2828,\ ;disabled button
|
||||
2E2F,\ ;focused button
|
||||
2E2B,\ ;default button
|
||||
0030,\ ;list item
|
||||
002F,\ ;focused list item
|
||||
003F,\ ;selected list item
|
||||
003E,\ ;marked list item
|
||||
002E,\ ;marked focused list item
|
||||
002F,\ ;active dialog caption
|
||||
002F,\ ;not active dialog caption
|
||||
001F,\ ;input
|
||||
003F,\ ;input selection
|
||||
0078,\ ;disabled input
|
||||
00FE,\ ;modified input
|
||||
0013,\ ;scrollbar
|
||||
3E30,\ ;cluster
|
||||
3F3F,\ ;cluster highlighted
|
||||
7878,\ ;cluster disabled
|
||||
001F,\ ;active window frame
|
||||
0017,\ ;not active window frame
|
||||
001F,\ ;active window title
|
||||
0017,\ ;not active window title
|
||||
001E,\ ;column title
|
||||
1E1F,\ ;item in window
|
||||
002F,\ ;focused item in window
|
||||
001A,\ ;selected item in window
|
||||
001E,\ ;marked item in window
|
||||
003E,\ ;focused marked item in window
|
||||
3E30,\ ;status line
|
||||
7E70,\ ;dialog box text
|
||||
3F3B,\ ;dialog column header
|
||||
1F1E,\ ;window column header
|
||||
0071,\ ;group/tab frame color
|
||||
1E1F,\ ;window button
|
||||
1818,\ ;window disabled button
|
||||
2E2F,\ ;window focused button
|
||||
1E1A,\ ;window default button
|
||||
1E1F,\ ;window cluster
|
||||
1E1F,\ ;window cluster highlighted
|
||||
1818 ;window cluster disabled
|
||||
|
||||
colors#=\ ;gray scheme
|
||||
7470,\ ;menubox
|
||||
F4F0,\ ;menubar
|
||||
040F,\ ;highlighted menu item
|
||||
7878,\ ;disabled menu item
|
||||
F4F0,\ ;button
|
||||
F7F7,\ ;disabled button
|
||||
F4F0,\ ;focused button
|
||||
F4F0,\ ;default button
|
||||
008F,\ ;list item
|
||||
000F,\ ;focused list item
|
||||
008B,\ ;selected list item
|
||||
008E,\ ;marked list item
|
||||
000E,\ ;marked focused list item
|
||||
00F0,\ ;active dialog caption
|
||||
00F8,\ ;not active dialog caption
|
||||
008F,\ ;input
|
||||
0080,\ ;input selection
|
||||
0078,\ ;disabled input
|
||||
008E,\ ;modified input
|
||||
008F,\ ;scrollbar
|
||||
7470,\ ;cluster
|
||||
7470,\ ;cluster highlighted
|
||||
7878,\ ;cluster disabled
|
||||
008F,\ ;active window frame
|
||||
008F,\ ;not active window frame
|
||||
008F,\ ;active window title
|
||||
008F,\ ;not active window title
|
||||
008B,\ ;column title
|
||||
8B8F,\ ;item in window
|
||||
000F,\ ;focused item in window
|
||||
008B,\ ;selected item in window
|
||||
008E,\ ;marked item in window
|
||||
000E,\ ;focused marked item in window
|
||||
0070,\ ;status line
|
||||
7470,\ ;dialog box text
|
||||
8F8B,\ ;dialog column header
|
||||
8F8B,\ ;window column header
|
||||
0070,\ ;group/tab frame color
|
||||
8E8F,\ ;window button
|
||||
8787,\ ;window disabled button
|
||||
0E0F,\ ;window focused button
|
||||
8E8B,\ ;window default button
|
||||
8E8F,\ ;window cluster
|
||||
8E8F,\ ;window cluster highlighted
|
||||
8787 ;window cluster disabled
|
||||
|
||||
colors#=\ ;blue scheme
|
||||
3F30,\ ;menubox
|
||||
3F30,\ ;menubar
|
||||
0F0F,\ ;highlighted menu item
|
||||
3838,\ ;disabled menu item
|
||||
7470,\ ;button
|
||||
7878,\ ;disabled button
|
||||
3F3F,\ ;focused button
|
||||
3F3F,\ ;default button
|
||||
001B,\ ;list item
|
||||
003F,\ ;focused list item
|
||||
001F,\ ;selected list item
|
||||
001E,\ ;marked list item
|
||||
003E,\ ;marked focused list item
|
||||
003F,\ ;active dialog caption
|
||||
003F,\ ;not active dialog caption
|
||||
001F,\ ;input
|
||||
003F,\ ;input selection
|
||||
0078,\ ;disabled input
|
||||
00FE,\ ;modified input
|
||||
001F,\ ;scrollbar
|
||||
7470,\ ;cluster
|
||||
7470,\ ;cluster highlighted
|
||||
7878,\ ;cluster disabled
|
||||
001B,\ ;active window frame
|
||||
001B,\ ;not active window frame
|
||||
001F,\ ;active window title
|
||||
001B,\ ;not active window title
|
||||
001E,\ ;column title
|
||||
1E1B,\ ;item in window
|
||||
003F,\ ;focused item in window
|
||||
001F,\ ;selected item in window
|
||||
001E,\ ;marked item in window
|
||||
003E,\ ;focused marked item in window
|
||||
0030,\ ;status line
|
||||
7470,\ ;dialog box text
|
||||
1B1E,\ ;dialog column header
|
||||
1F1E,\ ;window column header
|
||||
0073,\ ;group/tab frame color
|
||||
1E1B,\ ;window button
|
||||
1818,\ ;window disabled button
|
||||
3F3F,\ ;window focused button
|
||||
1E1F,\ ;window default button
|
||||
1E1B,\ ;window cluster
|
||||
1E1B,\ ;window cluster highlighted
|
||||
1818 ;window cluster disabled
|
||||
|
|
@ -47,7 +47,7 @@ prepare() {
|
|||
patch --directory="$_sourcedir" <"$srcdir/$(basename ${file})"
|
||||
fi
|
||||
done
|
||||
sed 's/Liberation Mono/Hack/;s/float alpha = 0.8;/float alpha = 0.85;/' "$_sourcedir/config.def.h" > "$_sourcedir/config.h"
|
||||
sed 's/Liberation Mono/Hack/;s/float alpha = 0.8;/float alpha = 0.55;/' "$_sourcedir/config.def.h" > "$_sourcedir/config.h"
|
||||
|
||||
# This package provides a mechanism to provide a custom config.h. Multiple
|
||||
# configuration states are determined by the presence of two files in
|
||||
|
|
|
|||
|
|
@ -15,21 +15,18 @@ syslinux
|
|||
tigervnc
|
||||
wpa_supplicant
|
||||
|
||||
# hardinfo-gtk3 / opensuperclone-git
|
||||
cmake
|
||||
|
||||
# iwgtk
|
||||
gtk4
|
||||
meson
|
||||
qrencode
|
||||
scdoc
|
||||
|
||||
# python-mariadb-connector
|
||||
mariadb-libs
|
||||
|
||||
# smartmontools-svn
|
||||
subversion
|
||||
|
||||
# udevil
|
||||
gettext
|
||||
intltool
|
||||
|
||||
# wd719x-firmware
|
||||
lha
|
||||
|
|
|
|||
3
setup/linux/profile/airootfs/etc/adjtime
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
0.0 0 0
|
||||
0
|
||||
LOCAL
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIGTzCCBDegAwIBAgIBfDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMCVVMx
|
||||
DzANBgNVBAgTBk9yZWdvbjERMA8GA1UEBxMIUG9ydGxhbmQxHTAbBgNVBAoTFDEy
|
||||
MDEgQ29tcHV0ZXIgUmVwYWlyMSMwIQYDVQQLExoxMjAxIENlcnRpZmljYXRlIEF1
|
||||
dGhvcml0eTEVMBMGA1UEAxMMMTIwMSBSb290IENBMSIwIAYJKoZIhvcNAQkBFhNt
|
||||
YW5hZ2VtZW50QDEyMDEuY29tMB4XDTE4MDgyMDA2MDEwMFoXDTM4MDgyMDA2MDEw
|
||||
MFowgbAxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZPcmVnb24xETAPBgNVBAcTCFBv
|
||||
cnRsYW5kMR0wGwYDVQQKExQxMjAxIENvbXB1dGVyIFJlcGFpcjEjMCEGA1UECxMa
|
||||
MTIwMSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFTATBgNVBAMTDDEyMDEgUm9vdCBD
|
||||
QTEiMCAGCSqGSIb3DQEJARYTbWFuYWdlbWVudEAxMjAxLmNvbTCCAiIwDQYJKoZI
|
||||
hvcNAQEBBQADggIPADCCAgoCggIBANGYohJk5/CC/p14R7EpnhdEUF7Wvlnt8yuF
|
||||
dtuyStlIGkLxPMlj9hQfoLDplQqlKBefTaI3WwrI/Hndso+jStLKgtRWRdyNB34K
|
||||
AWqT04zXYGicdi3fqaMhEC4SPyX1tRXU2e9kjtIJ21AZx2F40NUjfOMKLVymZgXm
|
||||
gkG1oA/BSzE8vIidrd/lJPwo0u+EYFa87y+9SHS93Ze1AVoTVqUzSMkjqt+6YIzJ
|
||||
4XBD7UBvps0Mnd18HMUlXHFXusUL1K921W3wDVcMlNIIA8MJjQk+aVS/1EGSn+81
|
||||
C+r40x64lYkyh0ZUAHkVXUC/BUfa0SKx1Nfa4mSvtyPnUCb7Dir8MkTDKgopGCok
|
||||
KmW+VvE2H8AEPCbcctFmhdip19laYxzyDhZ5wiQN6AOg64cWvDf6/uT9hyPvxkj1
|
||||
ps5vWElryzawTE7h1BI8liMqwsG1Y7cc6D0PABxPsp4iR8pde0oZtpLnEvejRodo
|
||||
zz3BGvZjq+pHtRMjL+yiDtdAL+K+7/e7gNCQBIGsphahWIOf7TczWVgMNclTNxl3
|
||||
WZjKkOEs7j+prRTDvffV6H32+Tk5TwgMsfvnY4a37CkJ0L0d1JhWj9wO+gESfg3W
|
||||
8yvt3hfcj3NOUMJWhJstqlIeX8dj7vVcMhjNvYJxabJmJgk+DNlHe55fXDGJ1CLO
|
||||
E0EbRTyBAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFM+hXjFx
|
||||
6BldZFBQW1Pn/Yp3vbw+MAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAAcw
|
||||
HgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOC
|
||||
AgEALWcnu3auMSnSSF/kOiLvJ4RAnHZebGYNcUWM14u1K1/XtTB7AFzQIHX7BcDH
|
||||
m/z4UEyhl9EdR5Bgf2Szuk+8+LyGqcdAdbPoK+bmcwwL8lufDnlIYBThKIBfU2Xw
|
||||
vw41972B+HH5r1TZXve1EdJaLyImbxmq5s41oH7djGC+sowtyGuVqP7RBguXBGiJ
|
||||
At1yfdPWVaxLmE8QFknkIvpgTmELpxasTfvgnQBenA3Ts0Z2hwN4796hLbRzGsb8
|
||||
4hKWAfQDP0klzXKRRyVeAueXxj/FcNZilYxv15MqMc4qrUiW0hXHluQM1yceNjNZ
|
||||
SE4Igi1Ap71L4PpgkHIDfZD908UexGGkql+p4EWrpnXUYWTa0sHg1bFKQntgpyFg
|
||||
86Ug0Q7ZNhImENzeigZknL0ceIdaNUCs7UPrkqpUSJR2yujp1JC3tX1LgKZw8B3J
|
||||
fQx/8h3zzNuz5dVtr1wUJaUD0nGhMIRBEXb2t4jupEISSTN1pkHPcbNzhAQXjVUA
|
||||
CJxnnz3jmyGsNCoQf7NWfaN6RSRTWehsC6m7JvPvoU2EZoQkSlNOv4xZuFpEx0u7
|
||||
MFDtC1cSGPH7YwYXPVc45xAMC6Ni8mvq93oT89XZNHIqE8/T8aPHLwYFgu1b1r/A
|
||||
L8oMEnG5s8tG3n0DcFoOYsaIzVeP0r7B7e3zKui6DQLuu9E=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -30,6 +30,7 @@ alias rmdirs='find -depth -mindepth 1 -type d -exec rmdir "{}" --ignore-fail-on-
|
|||
alias rs='rsync -avhPS --stats --exclude-from="$HOME/.rsync_exclusions"'
|
||||
alias rsz='rsync -avhzPS --stats --exclude-from="$HOME/.rsync_exclusions"'
|
||||
alias sdu='sudo du -sch --apparent-size'
|
||||
alias set_lp8550_slope='sudo set_lp8550_slope.py'
|
||||
alias srmdirs='sudo find -depth -mindepth 1 -type d -exec rmdir "{}" --ignore-fail-on-non-empty \;'
|
||||
alias srs='sudo rsync -avhPS --stats --exclude-from="$HOME/.rsync_exclusions"'
|
||||
alias srsz='sudo rsync -avhzPS --stats --exclude-from="$HOME/.rsync_exclusions"'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
Signature: 8a477f597d28d172789f06886806bc55
|
||||
# This file is a cache directory tag created by fontconfig.
|
||||
# For information about cache directory tags, see:
|
||||
# http://www.brynosaurus.com/cachedir/
|
||||
|
|
@ -88,6 +88,7 @@ ${memgraph}
|
|||
#Network
|
||||
${alignc}S H O R T C U T K E Y S
|
||||
${hr}
|
||||
[Super] + c${alignr}WK Clone
|
||||
[Super] + d${alignr}HW Diagnostics
|
||||
[Super] + f${alignr}File Manager
|
||||
[Super] + i${alignr}HW Information
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<execute>gsmartcontrol_polkit</execute>
|
||||
</action> </item>
|
||||
<item label="Web Browser"> <action name="Execute">
|
||||
<execute>firefox</execute>
|
||||
<execute>netsurf</execute>
|
||||
</action> </item>
|
||||
<separator/>
|
||||
<menu id="all-items-menu" label="All Programs">
|
||||
|
|
@ -75,8 +75,8 @@
|
|||
<item label="ELinks"> <action name="Execute">
|
||||
<execute>st -e elinks</execute>
|
||||
</action> </item>
|
||||
<item label="FireFox"> <action name="Execute">
|
||||
<execute>firefox</execute>
|
||||
<item label="NetSurf"> <action name="Execute">
|
||||
<execute>netsurf</execute>
|
||||
</action> </item>
|
||||
<item label="FTP"> <action name="Execute">
|
||||
<execute>st -e ftp</execute>
|
||||
|
|
@ -124,8 +124,8 @@
|
|||
<item label="MC"> <action name="Execute">
|
||||
<execute>st -e mc</execute>
|
||||
</action> </item>
|
||||
<item label="Thunar File Manager"> <action name="Execute">
|
||||
<execute>thunar</execute>
|
||||
<item label="PCMan File Manager"> <action name="Execute">
|
||||
<execute>pcmanfm</execute>
|
||||
</action> </item>
|
||||
<item label="st"> <action name="Execute">
|
||||
<execute>st</execute>
|
||||
|
|
@ -139,7 +139,7 @@
|
|||
</action> </item>
|
||||
</menu>
|
||||
<item label="File Manager"> <action name="Execute">
|
||||
<execute>thunar-settings</execute>
|
||||
<execute>pcmanfm</execute>
|
||||
</action> </item>
|
||||
<item label="Preferred Applications"> <action name="Execute">
|
||||
<execute>exo-preferred-applications</execute>
|
||||
|
|
@ -151,9 +151,6 @@
|
|||
<item label="Avahi Zeroconf Browser"> <action name="Execute">
|
||||
<execute>avahi-discover</execute>
|
||||
</action> </item>
|
||||
<item label="Bulk Rename"> <action name="Execute">
|
||||
<execute>/usr/lib/Thunar/ThunarBulkRename</execute>
|
||||
</action> </item>
|
||||
<item label="Conky"> <action name="Execute">
|
||||
<execute>st -e conky</execute>
|
||||
</action> </item>
|
||||
|
|
|
|||
|
|
@ -295,6 +295,11 @@
|
|||
<menu>root-menu</menu>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-c">
|
||||
<action name="Execute">
|
||||
<command>st -t "WKClone (ddrescue-tui)" -e resize-and-run ddrescue-tui clone</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-d">
|
||||
<action name="Execute">
|
||||
<command>st -t "Hardware Diagnostics" -e resize-and-run hw-diags</command>
|
||||
|
|
@ -302,12 +307,12 @@
|
|||
</keybind>
|
||||
<keybind key="C-A-f">
|
||||
<action name="Execute">
|
||||
<command>thunar</command>
|
||||
<command>pcmanfm</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-i">
|
||||
<action name="Execute">
|
||||
<command>hardinfo</command>
|
||||
<command>st -t "Hardware Info" -e hw-info-helper</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-m">
|
||||
|
|
@ -337,7 +342,7 @@
|
|||
</keybind>
|
||||
<keybind key="C-A-w">
|
||||
<action name="Execute">
|
||||
<command>firefox</command>
|
||||
<command>netsurf</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-x">
|
||||
|
|
@ -345,6 +350,11 @@
|
|||
<command>menu_powermenu</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-c">
|
||||
<action name="Execute">
|
||||
<command>st -t "WKClone (ddrescue-tui)" -e resize-and-run ddrescue-tui clone</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-d">
|
||||
<action name="Execute">
|
||||
<command>st -t "Hardware Diagnostics" -e resize-and-run hw-diags</command>
|
||||
|
|
@ -352,12 +362,12 @@
|
|||
</keybind>
|
||||
<keybind key="W-f">
|
||||
<action name="Execute">
|
||||
<command>thunar</command>
|
||||
<command>pcmanfm</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-i">
|
||||
<action name="Execute">
|
||||
<command>hardinfo</command>
|
||||
<command>st -t "Hardware Info" -e hw-info-helper</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-m">
|
||||
|
|
@ -387,7 +397,7 @@
|
|||
</keybind>
|
||||
<keybind key="W-w">
|
||||
<action name="Execute">
|
||||
<command>firefox</command>
|
||||
<command>netsurf</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-x">
|
||||
|
|
|
|||
7
setup/linux/profile/airootfs/etc/skel/.ssh/config
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
ServerAliveInterval 120
|
||||
|
||||
Host *
|
||||
LogLevel=quiet
|
||||
StrictHostKeyChecking no
|
||||
UserKnownHostsFile=/dev/null
|
||||
|
||||