├── .github └── FUNDING.yml ├── COPYING ├── README.md ├── data ├── home.conf ├── imglist ├── mirrors ├── mountpoint ├── part-table.img ├── vdesktop.conf ├── version └── ziplist ├── flash ├── functions ├── advmount ├── buffer ├── edit ├── image-shrink ├── imglist-parser ├── milliways-image-backup ├── restore-pt ├── terminal-run ├── zerofree └── zerofree_runner ├── home ├── icons ├── back.png ├── backup.png ├── boot_mode.gif ├── burn.png ├── check.png ├── download.png ├── exit.png ├── expand.png ├── forward.png ├── gparted.png ├── home.png ├── img-16.png ├── img-48.png ├── img-64.png ├── install.png ├── link.png ├── logo-16.png ├── logo-64.png ├── logo-tall.png ├── mounted.png ├── power.png ├── refresh.png ├── rpi-logo.png ├── save.png ├── sd-to-img.png ├── search.png ├── settings.png ├── shrink.png ├── tool.png ├── toolbox.png ├── trash.png ├── twister.png ├── unmounted.png ├── usb-16.png ├── usb-64.png ├── usb-mount.png ├── usb-unmount.png └── vector │ ├── burn.png │ ├── diskimage-small.svg │ ├── diskimage.svg │ ├── download.svg │ ├── fire-logo.svg │ ├── gparted.svg │ ├── green-dot.svg │ ├── install.svg │ ├── logo-tall.svg │ ├── logo.svg │ ├── power.png │ ├── power.svg │ ├── red-dot.svg │ ├── save.svg │ ├── search.png │ ├── search.svg │ ├── tool.png │ ├── tool.svg │ ├── toolbox.svg │ ├── usb.svg │ ├── vd-tall.svg │ └── vd.svg ├── img-mode ├── installgui ├── update └── usb-mode /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://paypal.me/josephmarchand 2 | 3 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pi Power Tools ![logo](https://github.com/Botspot/Pi-Power-Tools/blob/master/icons/logo-64.png?raw=true) 2 | [![badge](https://github.com/Botspot/pi-apps/blob/master/icons/badge.png?raw=true)](https://github.com/Botspot/pi-apps) 3 | ## General Purpose Raspbian Image & SD Card Manager 4 | Want to **build your own custom Raspbian SD card image**? Like enabling ssh, modifying `/boot/config.txt`, doing an `apt update`, enabling VNC, or installing something? 5 | Have a pile of sd cards and curious what's on them? (and don't want to boot them, one at a time, in a Pi?) 6 | Want to **run two versions of Raspbian** at once? 7 | Or, how about **running Raspbian Stretch on a Pi 4**? 8 | ### No other tool can do any of these: 9 | - **Boot** - Powered by [Vdesktop](https://github.com/Botspot/vdesktop). Runs the Raspbian image in a virtual machine. It even shows the desktop! 10 | - **Flash** - Flashes Raspbian directly from the Internet to the selected device. Tuned for maximum speed, [proven by benchmarks](https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=272365). 11 | - **Mount** - Full control to manage loop devices and mountpoints. 12 | - **Edit** - Auto-creates a loop device, then lets you mess up its partitions with Gparted. 13 | - **Resize** - Add or remove free space from a disk image with a single click. <-- This also repairs the image! 14 | 15 | Sneak peek: 16 | ![img mode](https://i.ibb.co/yQnT2sC/img-mode.png) 17 | Want to give it a spin? Great! 18 | ## To download & install 19 | Run this command in a terminal in the desktop session, [not from the console](https://github.com/Botspot/Pi-Power-Tools/issues/3#issuecomment-791468668): 20 | ``` 21 | wget -O - https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/master/update | bash 22 | ``` 23 | This clones the repository to `/home/pi/Pi-Power-Tools`, adds a menu button, clones my [vdesktop repo](https://github.com/Botspot/vdesktop), and asks permission before installing YAD. Nothing is modified outside of your home directory. 24 | #### System requirements: 25 | - Raspberry Pi 3B, 3B+, or 4B (untested on older models) 26 | - Running Raspbian Buster or Stretch 27 | - Passwordless `sudo` ability for `pi` user 28 | - Internet connection (recommended for update checking & img downloading) 29 | 30 | After installing, Pi Power Tools can be launched from the Menu: 31 | ![menu](https://i.ibb.co/PQthp6N/menu.png) 32 | 33 | ## To uninstall: 34 | If it's not working for you, please [open an issue](https://github.com/Botspot/Pi-Power-Tools/issues/new). 35 | ``` 36 | sudo apt purge gparted yad systemd-container xserver-xephyr expect 37 | rm -rf ${HOME}/Pi-Power-Tools ${HOME}/Pi-Power-Tools.old 38 | rm ${HOME}/Desktop/ppt.desktop ${HOME}/.local/share/applications/ppt.desktop 39 | ``` 40 | --- 41 | # Instructions 42 | ### Table of Contents 43 | - [Home](#home) 44 | - [Settings](#settings) 45 | - [IMG Mode](#img-mode) 46 | - [USB Mode](#usb-mode) 47 | - [Flash](#flash) 48 | - [Button explanations](#explanations-for-some-buttons) 49 | - [Boot](#boot) 50 | - [View](#view) 51 | - [Edit](#edit) 52 | - [Advanced Mount](#advanced-mount) 53 | - [Under the hood](#under-the-hood) 54 | - [Directory Tree](#directory-tree) 55 | - [Q&A](#qa) 56 | ## Home 57 | ![home page](https://i.ibb.co/vz4yR40/home.png) 58 | The menu button opens this window by default. Enter [IMG Mode](https://github.com/Botspot/Pi-Power-Tools#img-mode) or [USB Mode](https://github.com/Botspot/Pi-Power-Tools#usb-mode) from here. The Update button will appear if an update is available. 59 | ### Settings 60 | ![settings](https://i.ibb.co/Cz9YMDj/settings.png) 61 | Currently, you can change what the menu button opens by default - (Flash, IMG Mode, USB Mode, and Home), if the Boot Button launches the Desktop, whether or not to run `zerofree` when the Shrink Button is clicked, and how much free space to add when the 'X GB free' button is clicked. 62 | 63 | 64 | ## IMG Mode 65 | Customize Raspbian Images. 66 | ![img mode](https://i.ibb.co/yQnT2sC/img-mode.png) 67 | Before you can see that above page, you first have to select an img. 68 | ![img mode page 1](https://i.ibb.co/0sp9NMz/2020-11-27-092634-1920x1080-scrot.png) 69 | **4 ways** to select a Raspbian Image: 70 | 1. **Click the arrow** on the right to see any previously used disk images: 71 | ![arrow](https://i.ibb.co/tH9qLTQ/arrow.png) 72 | 1. **Drag-n-drop** a Raspbian image from File Manager to the text box: 73 | ![drag and drop](https://i.ibb.co/PmFWQzh/dnd.png) 74 | 1. Click **Download New** to download & unzip a new Raspbian image: 75 | ![download](https://i.ibb.co/XD8FgkN/download.png) 76 | Download mode has been tuned for **maximum speed**. 77 | ![benchmark](https://i.ibb.co/25KV76J/benchmark.png) 78 | 1. Or create an img from your running Pi by clicking SD to IMG. 79 | ![sd to img](https://i.ibb.co/pR2dH2N/snapshot.png) 80 | 81 | After a disk image has been selected, this main page appears: 82 | ![img mode](https://i.ibb.co/yQnT2sC/img-mode.png) 83 | **Buttons**: 84 | - **Back**: Back to the image selection window. 85 | - **Flash**: Copy everything from the selected img to a device. [Details](#flash) 86 | - **Boot**: Attempt to "boot" the selected device using a virtual desktop. [Details](#boot) 87 | - **View**: Mounts the image to `/media/pi/pi-power-tools` so you can modify the filesystem. [Details](#view) 88 | - **Edit**: Modify the partitions using Gparted. [Details](#edit) 89 | - **Reset PT**: This button *attempts* to fix the partitions if you messed them up somehow. It replaces the partition table with the one from Raspbian Buster Full. Your Mileage May Vary. 90 | - **Advmount**: Control exactly where to mount each partition. In theory this will work for any kind of disk image, but has only been tested on Raspbian images. YMMV. [Details](#advanced-mount) 91 | - **Shrink**: Removes all free space from the selected image. If enabled in [Settings](#Settings), `zerofree` will run afterwards to remove unused blocks from the disk image. 92 | - Though similar in function, the script used to do this is entirely different in design and does not utilize RonR's `image-utils` in any way. 93 | - **1 GB Free**: Adds one gigabyte of free space to the root partition. Customize how much free space in [Settings](https://github.com/Botspot/Pi-Power-Tools#settings). 94 | - It is **not accumulative**, so clicking it multiple times won't result in multiple gigabytes of free space. 95 | ## USB Mode 96 | Manage storage devices connected to your Pi. 97 | ![usb mode](https://i.ibb.co/vJ68mRY/usb.png) 98 | Select a drive in the list, then click an action button on the bottom. 99 | *Your Pi's internal SD Card (`/dev/mmcblk0`) cannot be flashed or booted.* 100 | **Buttons**: 101 | - **Home**: Back to the [home window](#home). 102 | - **Refresh**: Check for any newly inserted storage devices. 103 | - **Flash**: Copy everything from an img to the selected device. [Details](#flash) 104 | - **Boot**: Attempt to "boot" the selected device using a virtual desktop. [Details](#boot) 105 | - **View**: Mounts the device to `/media/pi/pi-power-tools`. [Details](#view) 106 | *Intended for Raspbian devices only, as this button assumes there are 2 partitions. (and mounts partition 1 to `/media/pi/pi-power-tools/boot`)* 107 | - **Edit**: Modify the partitions using Gparted. [Details](#edit) 108 | ## Flash 109 | ![flash tool](https://i.ibb.co/YcrCspx/flash.png) 110 | Select an input disk image and an output usb drive. The filesystem root device (`/dev/mmcblk0`) cannot be flashed and so is not listed. 111 | In addition to any previously used img and zip files, there are **three download options**: 112 | ![flash download options](https://i.ibb.co/3RRfnrR/flash-dl-opts.png) 113 | If Download is selected, this window will appear next asking which download mode you want. 114 | ![Flash tool second page](https://i.ibb.co/dmL3b7s/flash2.png) 115 | The first mode is much faster than the second one. 116 | Breakdown of the pros and cons: 117 | | Faster mode | Slower mode| 118 | |--|--| 119 | |For Pies that don't have sufficient free space to hold a large disk image|You keep the downloaded image for later.| 120 | |Better for a single flash|Better for flashing multiple SD cards 121 | 122 | Concerning speed, here are benchmarks made on a 4GB Pi 4: 123 | ||Faster Mode:|Slower mode:| 124 | |--|--|--| 125 | |Raspbian Full|387 seconds|816 seconds| 126 | |Raspbian|205|393| 127 | |Raspbian Lite|102|184| 128 | --- 129 | # Explanations for some buttons: 130 | ## Boot 131 | With this handy button, you can "test drive" your img/usb without needing to power down, switch sd cards, etc. Any changes made inside (`apt install`, change preferences, etc) will be preserved in the img/usb. 132 | 133 | > Instead of a Virtual Machine, this technique runs at **100% native speed** (no emulation), because the guest runs the same kernel as the host device. Therefore, only booting Raspbian is supported, as **other OSes require other kernels**. 134 | > That means this method will still work just fine, *even if the whole boot partition is corrupted*! 135 | 136 | First, the VM's Console will appear and display the boot text. 137 | After the boot process has completed, and if "Boot to Desktop" is enabled in [Settings](#settings), a window similar to VNC will open and display the desktop. 138 | ![animated boot button](https://github.com/Botspot/Pi-Power-Tools/blob/master/icons/boot_mode.gif?raw=true) 139 | 140 | > It is a [known issue](https://github.com/Botspot/vdesktop/issues/3) that the browser crashes with the `Aw, Snap!` error. 141 | 142 | ## View 143 | Mounts the selected device/img to `/media/pi/pi-power-tools`. 144 | ![view dialog](https://i.ibb.co/RgbKJff/view-files.png) 145 | When you close the `Viewing /dev/sdX` dialog window, the selected device will be unmounted (ejected). 146 | 147 | ## Edit 148 | Lets you edit the partitions of the selected device using `gparted`. 149 | ![gparted opening a disk image](https://i.ibb.co/pww2C9h/gparted.png) 150 | 151 | ## Advanced Mount 152 | Fine-tune control over loop devices and mountpoints for disk images. 153 | If there are multiple loop devices associated with it, you will be prompted to select one and delete the others. 154 | Which one to delete? Usually you want to keep the top one on the list. (in this example, that's `/dev/loop0`) 155 | 156 | ![image-utils settings tab](https://i.ibb.co/ftn0JXz/loop-select.png) 157 | Now you can mount each partition where you want to: 158 | ![image-utils settings tab](https://i.ibb.co/PQXPL1M/advmount.png) 159 | When you're finished, be sure to **click Delete** to detach the loop device. 160 | 161 | --- 162 | ## Under the hood 163 | Pro tip: *There are many comments in the shell scripts. Not only does this assist debugging, it also makes most of it self-explanatory.* 164 | ### Directory Tree: 165 | 166 | **Pi-Power-Tools/** - This is the main folder that stores everything. 167 | > During an update, the old folder is renamed to Pi-Power-Tools.old/ 168 | - Scripts: 169 | - **installgui** - This prompts for packages to be installed 170 | - **flash** - This is the [Flash tool](#flash)'s script. 171 | - **update** - This script is what installs/updates Pi Power Tools. 172 | - **home** - This is [Home](#home)'s script. 173 | - **img-mode** - This script is [IMG Mode](#img-mode). 174 | - **usb-mode** - This script is [USB Mode](#usb-mode). 175 | - Other files: 176 | - **installedpackages** - Keeps a record of what the update script installed. 177 | - **README.md** - You are reading [this](https://github.com/Botspot/Pi-Power-Tools/blob/master/README.md) right now. 178 | - Folders: 179 | - **functions/** - Stores sub-scripts that do certain things. 180 | - **advmount** - The [Advanced Mount](#advanced-mount) tool. 181 | - **buffer** - The secret sauce behind the fast download speeds. This ARMHF executable ships with Pi Power Tools, to prevent adding a dependency for installing `buffer`. 182 | - **edit** - A short bash script that opens a Raspbian image in `gparted`. It handles creating a loop device and deleting it when done. 183 | - **imglist-parser** - Bash script that parses `imglist`, deletes entries that don't exist on the filesystem, removes duplicate entries, and `echo`'s the resulting output. 184 | - **milliways-image-backup** - Fork of a fork of RonR's image-backup. I have made substancial changes to most of the script. 185 | - **restore-pt** - Short bash script to overwrite the specified disk image's partition table with `part-table.img`. 186 | - **terminal-run** - This simple script is used to run bash scripts in a terminal. It's a standardized workaround originally developed for [Pi-Apps](https://github.com/Botspot/pi-apps). 187 | - **image-shrink** - Botspot's version of `image-shrink`. Added a couple bug fixes and removed the interactive elements for better scripting usage. 188 | - **zerofree** - Another ARMHF executable for recognizing unused blocks and overwriting them with zeros. Shrinks a disk image even more. Only used if enabled in [Settings](#settings). 189 | - **zerofree_runner** - Wrapper bash script to interface with `zerofree`. Creates a loop device for the specified img, runs `zerofree`, runs `shrinkimage`, then removes the loop device. 190 | - **data/** - Stores configuration files 191 | - **home.conf** - Stores the configuration from the [Settings window](#settings). 192 | - **imglist** - Keeps track of what disk images have been used. 193 | - **mirrors** - URLs, sizes, and names of download sites. Feel free to add your own. 194 | - **mountpoint** - Change where to mount to. Default is `/media/pi/pi-power-tools`. 195 | - **part-table.img** - The default partition table extracted from Raspbian Buster Full. Used by the Reset PT button in [IMG Mode](#img-mode). 196 | - **vdesktop.conf** - Stores the boot mode for `vdesktop`. Possible values are `gui`, `cli`, and `cli-login`. 197 | - **version** - The new location for the version file. Compared against the online version to check for updates. 198 | - **ziplist** - Exactly the same as `imglist`, but stores entries for previously used ZIP files instead. Currently only used by the [Flash tool](#flash). 199 | - **icons/** - Stores all the icons for the user interface. 200 | - **vdesktop/** - Ships empty, but `installgui` populates this folder with all files from the [Vdesktop repo](https://github.com/Botspot/vdesktop). 201 | 202 | ### Basic script design: 203 | - The scripts all use YAD to handle the user interface.* I found that Zenity was way too limited. (and didn't allow for multiple buttons, button icons, customized tooltips, or much else) 204 | `*` Okay, `installgui` and `update` *do* use zenity because Raspbian does not include YAD by default. 205 | 206 | ## Q&A 207 | - Q: Unique logo.![logo](https://github.com/Botspot/Pi-Power-Tools/blob/master/icons/logo-64.png?raw=true)What inspired you? 208 | > It's a combination of BB-8, the RPi logo, and a saw blade. Also I think it looks a bit like like a pineapple, which is my favorite fruit. 209 | - What made you develop this tool? 210 | > I while back, I wanted to download Raspbian, uninstall all programming tools from it, do an `apt update` & `upgrade`, add a chrome extension to the browser, then flash it to a SD card. It took *several days* to fumble around with terminal commands. The action was so simple, but the process so hard that I realized a tool to do it was sorely needed. 211 | - Q: How long did it take to program this? 212 | > Several months. But it saved me enough time to be worth it. And I had a blast programming it all. 213 | -------------------------------------------------------------------------------- /data/home.conf: -------------------------------------------------------------------------------- 1 | 2 | This window 3 | FALSE 4 | 5 | Desktop 6 | 7 | TRUE 8 | 1 9 | -------------------------------------------------------------------------------- /data/imglist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/data/imglist -------------------------------------------------------------------------------- /data/mirrors: -------------------------------------------------------------------------------- 1 | #Want to add some custom image download locations yourself? 2 | #Just add them below. 3 | #The download must be in ZIP format, with the IMG being the only file in the ZIP. 4 | # 5 | #To find the extraction size of the online zipped img: 6 | #wget -qO- | dd bs=1M count=1 2>/dev/null | busybox unzip -lq - 2>/dev/null | sed -n '3p' | awk '{print $1}' 7 | # 8 | #Please note that technique only works if the extracted img is less than 4294967295 bytes due to busybox not supporting zip64 compression. 9 | #To find the extraction size of an online zipped image that's bigger than 4GB when extracted: (This will take a very long time) 10 | #wget -qO- | bsdtar -xvf - -O | wc -c 11 | # 12 | #Format: 13 | #NameUnzipped size in bytesURL <-- separated by tab characters 14 | # 15 | # Raspberry Pi OS (bullseye) 16 | Raspberry Pi OS Full 32-bit (bullseye) 9760145408 https://downloads.raspberrypi.org/raspios_full_armhf_latest 17 | Raspberry Pi OS 32-bit (bullseye) 3972005888 https://downloads.raspberrypi.org/raspios_armhf_latest 18 | Raspberry Pi OS Lite 32-bit (bullseye) 1954545664 https://downloads.raspberrypi.org/raspios_lite_armhf_latest 19 | Raspberry Pi OS 64-bit (bullseye) 4093640704 https://downloads.raspberrypi.org/raspios_arm64_latest 20 | Raspberry Pi OS Lite 64-bit (bullseye) 1929379840 https://downloads.raspberrypi.org/raspios_lite_arm64_latest 21 | # 22 | # Raspberry Pi OS (buster) 23 | Raspberry Pi OS Full 32-bit (buster) 8602517504 https://downloads.raspberrypi.org/raspios_full_armhf/images/raspios_full_armhf-2021-05-28/2021-05-07-raspios-buster-armhf-full.zip 24 | Raspberry Pi OS 32-bit (buster) 3980394496 https://downloads.raspberrypi.org/raspios_armhf/images/raspios_armhf-2021-05-28/2021-05-07-raspios-buster-armhf.zip 25 | Raspberry Pi OS Lite 32-bit (buster) 1874853888 https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-05-28/2021-05-07-raspios-buster-armhf-lite.zip 26 | Raspberry Pi OS 64-bit (buster) 3779067904 https://downloads.raspberrypi.org/raspios_arm64/images/raspios_arm64-2020-08-24/2020-08-20-raspios-buster-arm64.zip 27 | Raspberry Pi OS Lite 64-bit (buster) 1816133632 https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2021-05-28/2021-05-07-raspios-buster-arm64-lite.zip 28 | # 29 | # Twister OS 30 | Twister OS 1.9.2 10793570816 https://archive.org/download/twister-osv-1-9-2/TwisterOSv1-9-2.zip 31 | -------------------------------------------------------------------------------- /data/mountpoint: -------------------------------------------------------------------------------- 1 | /media/pi/pi-power-tools 2 | -------------------------------------------------------------------------------- /data/part-table.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/data/part-table.img -------------------------------------------------------------------------------- /data/vdesktop.conf: -------------------------------------------------------------------------------- 1 | gui 2 | -------------------------------------------------------------------------------- /data/version: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /data/ziplist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/data/ziplist -------------------------------------------------------------------------------- /flash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIRECTORY="$(dirname $0)" 3 | 4 | #$1 can either be a img file, usb device, or blank. 5 | 6 | #ERRORBOX 7 | errorbox(){ 8 | #$1 is message 9 | #$2 is type: fatal or left blank 10 | if [ -z "$2" ];then 11 | yad --title='Pi Power Tools' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 12 | --text="Oh no!\n\n$1\nTry again?" \ 13 | --center --width=300 --text-align=center \ 14 | --button="OK!${DIRECTORY}/icons/forward.png:0" 2>/dev/null 15 | elif [ "$2" = 'fatal' ];then 16 | yad --title='Pi Power Tools' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 17 | --text="Fatal error:\n$1\nClosing in 30 seconds." \ 18 | --center --width=300 --text-align=center \ 19 | --timeout=30 --timeout-indicator=bottom \ 20 | --button="Close!${DIRECTORY}/icons/exit.png:0" 2>/dev/null 21 | exit 1 22 | else 23 | errorbox 'errorbox error' fatal 24 | fi 25 | } 26 | 27 | 28 | #if not blank, figure out what variable 1 is. 29 | if [ ! -z $1 ];then 30 | if [ -b $1 ];then 31 | mime=usb 32 | elif [ -f $1 ] && [[ "$1" == *".img" ]];then 33 | mime=img 34 | else 35 | errorbox "$1 not found. You may have unplugged it or deleted it." fatal 36 | fi 37 | else 38 | #if blank 39 | mime=blank 40 | fi 41 | 42 | #find which partition is mounted to filesystem root 43 | rootdev="$(sudo findmnt -nmo TARGET,SOURCE | grep "/ " | awk '{print $2}')" 44 | #find which device hosts that partition 45 | rootdev="/dev/$(lsblk -no pkname $rootdev)" 46 | #something fishy is going on if the concluded device does not exist. 47 | if [ ! -b $rootdev ];then 48 | echo "Could not detect which partition mounts filesystem root!" 49 | echo "Exiting" 50 | exit 1 51 | fi 52 | echo "Omitting root device from list: ${rootdev}" 53 | repeat=1 54 | while [ $repeat -eq 1 ];do 55 | #display waiting dialog 56 | (echo "# " 57 | sleep infinity)| yad --progress --pulsate --title="Scanning devices" \ 58 | --text="Scanning Devices..." --window-icon="${DIRECTORY}/icons/logo-64.png" \ 59 | --width=300 --height=100 --center --auto-close --auto-kill \ 60 | --no-buttons 2>/dev/null & 61 | child_pid=$! 62 | sleep 0.5 63 | 64 | # V lists all devices Remove junk output V V omit root device 65 | parted_out="$(sudo parted --script -lm | grep '/dev/*' | grep -v $rootdev )" 66 | kill $child_pid 67 | 68 | echo "parted out" 69 | device_names="$(echo $parted_out | tr ';' '\n' | awk -F ":" '{print $7}' )" 70 | device_paths="$(echo $parted_out | tr ';' '\n' | awk -F ":" '{print $1}' | tr -d ' ')" 71 | 72 | devicelist="$(paste -d ' ' <(echo "$device_paths") <(echo "$device_names") | tr '\n' '!')" 73 | devicelist="${devicelist::-1}" 74 | echo "Devicelist: ${devicelist}EOD" 75 | 76 | if [ $mime = img ];then 77 | echo "mime is img" 78 | from="--field=From::RO" 79 | fromdev="$1" 80 | to="--field=To::CB" 81 | todev="$devicelist" 82 | else 83 | echo "mime is usb or null" 84 | 85 | #parse imglist 86 | imglist="$(${DIRECTORY}/functions/imglist-parser || errorbox 'Failed to parse imglist!')" 87 | 88 | if [ ! -z "$imglist" ];then 89 | imglist="${imglist}!" 90 | fi 91 | 92 | echo "parsed imglist to ${imglist}EOI" 93 | 94 | #PARSE ZIPLIST 95 | ziplist='' 96 | #if ziplist exists, ensure all lines contain valid files 97 | if [ -f "${DIRECTORY}/data/ziplist" ];then #if ziplist exists 98 | #number of lines in file 99 | numlines=$(wc -l "${DIRECTORY}/data/ziplist" | awk '{print $1}') 100 | echo "Numlines: $numlines" 101 | if [ $numlines -gt 0 ];then #if ziplist contains any lines 102 | echo "$(sort "${DIRECTORY}/data/ziplist" | uniq)" > "${DIRECTORY}/data/ziplist" #remove duplicate lines 103 | i=1 104 | while [ $i -le $numlines ]; do #repeat until all lines are checked 105 | line=$(sed -n "${i}p" < "${DIRECTORY}/data/ziplist") #read that line of text file 106 | #echo $line 107 | if [ ! -f $line ];then #if line is file not found 108 | #echo "not exist!" 109 | sed -i "${i}d" "${DIRECTORY}/data/ziplist" #delete the line 110 | fi 111 | i=$((i+1)) #increase line number by 1 to check the next one 112 | done 113 | ziplist="$(cat ${DIRECTORY}/data/ziplist | tr '\n' '!')" 114 | fi 115 | else 116 | #create file 117 | echo '' > "${DIRECTORY}/data/ziplist" 118 | fi 119 | 120 | if [ ! -z "$ziplist" ];then 121 | ziplist="${ziplist::-1}" 122 | ziplist="${ziplist}!" 123 | fi 124 | echo "parsed ziplist to ${ziplist}EOZ" 125 | 126 | #mirrors list ignore commented lines 127 | mirrors="$(cat "${DIRECTORY}/data/mirrors" | grep -v ^\#)" 128 | 129 | 130 | #parse mirrors list 131 | fromdev="${ziplist}${imglist}$(echo "$mirrors" | awk '$0="Download "$0' | awk -F '\t' '{print $1}')" 132 | #convert line breaks to "!" 133 | fromdev="$(echo "$fromdev" | tr '\n' '!')" 134 | #remove ending "!" 135 | fromdev="${fromdev::-1}" 136 | 137 | from="--field=From::CBE" 138 | to="--field=To::CB" 139 | 140 | #make selected device appear top on the list 141 | # 142 | #convert ! to \n for grep to use --v get entry containing $1 exclude entry containing $1 convert back to "!" 143 | todev="$(echo "$devicelist" | tr '!' '\n' | grep "$(echo "$1")" | tr '\n' '!' )$(echo "$devicelist" | tr '!' '\n' | grep -v "$(echo "$1")" | tr '\n' '!' )" 144 | 145 | #remove residual line break (!) 146 | todev="${todev::-1}" 147 | 148 | echo "fromdev: $fromdev" 149 | echo "todev: $todev" 150 | fi 151 | 152 | if [ "$todev" = ' ' ];then 153 | flashbutton='' 154 | todev='No flash-to devices found. Plug 1 in, then click Refresh.' 155 | else 156 | flashbutton="--button=Flash!${DIRECTORY}/icons/burn.png!This will overwrite everything on the flash-to device. 157 | Please be careful.:0" 158 | fi 159 | 160 | output="$(yad --form --separator='\n' --center --width=500 --height=100 \ 161 | --title='Flash Tool' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 162 | --text="This flashes SD cards. ZIP and IMG files supported." \ 163 | "$from" "$fromdev" \ 164 | "$to" "$todev" \ 165 | --button='Cancel'!"${DIRECTORY}/icons/exit.png"!:1 \ 166 | --button='Refresh'!"${DIRECTORY}/icons/refresh.png"!'Refreshes the flash-to list.':2 \ 167 | "$flashbutton" \ 168 | 2>/dev/null)" 169 | button=$? 170 | 171 | FROM="$(echo "$output" | sed -n '1p')" 172 | TO="$(echo "$output" | sed -n '2p' | awk '{print $1}')" 173 | 174 | echo "From: $FROM" 175 | echo "To: $TO" 176 | 177 | if [ $button -eq 252 ] || [ $button -eq 143 ] || [ $button -eq 1 ];then 178 | #if window manager x was pressed 179 | # or if yad was killed 180 | # or if cancel was pressed 181 | #then exit the program 182 | exit 0 183 | fi 184 | 185 | if [ ! $button -eq 2 ];then 186 | repeat=0 187 | fi 188 | done 189 | 190 | 191 | getdevname(){ 192 | #convert device path to device name 193 | devname="$(lsblk -o name,path | grep "$TO" | awk '{print $1}' | head -n 1)" 194 | echo "Devname: ${devname}EOD" 195 | } 196 | 197 | umountsubdevs(){ 198 | #get subdevices 199 | partitions="$(lsblk $TO -no PATH | tail -n +2 | tr '\n' ' ')" 200 | echo "Subdevices: ${partitions}" 201 | #unmount subdevices twice in case one is a dependency of the other 202 | sudo umount $partitions 203 | sudo umount $partitions 204 | 205 | #check and make sure unmounted 206 | if [ ! -z "$(lsblk -no mountpoint $TO)" ];then 207 | errorbox "Failed to unmount $TO." fatal 208 | fi 209 | } 210 | 211 | if [ "$(echo "$FROM" | awk '{print $1}')" = "Download" ];then 212 | #Download mode 213 | 214 | #convert device path to device name 215 | devname="$(lsblk -o name,path | grep "$TO" | awk '{print $1}' | head -n 1)" 216 | echo "Devname: ${devname}EOD" 217 | 218 | #remove first word "Download" 219 | FROM="$(echo "$FROM" | cut -d' ' -f2-)" 220 | 221 | #convert user-friendly name to mirror URL 222 | 223 | #this gets the line number for the selected name 224 | linenumber="$(echo "$mirrors" | tr ' ' '\n' | grep -nx "$FROM" | cut -f1 -d:)" 225 | #this goes 2 line numbers lower, which turns out to be the line number for the URL 226 | linenumber=$((linenumber+2)) 227 | #and this turns that line number into what we want: the URL that corresponds with the selected name. 228 | URL="$( "$mirrors" | tr ' ' '\n' | sed -n "${linenumber}p")" 229 | 230 | echo "Download URL is $URL" 231 | 232 | #match it to relavent line in mirrors file 233 | #Get the line number of the relevant line in text file, then read the data from that line. 234 | # 235 | # get first entry so remove urls & size --v 236 | # v-- exact match & output line number in format "2:Raspbian Desktop" 237 | # v-- seperate out only line number 238 | linenumber="$(echo "$mirrors" | awk -F '\t' '{print $1}' | grep -xn "$FROM" | awk -F ':' '{print $1}' )" 239 | matchmirror="$(echo "$mirrors" | sed -n "${linenumber}p")" 240 | 241 | #prompt user for which download program 242 | output="$(echo -e 'Faster: Download, unzip, flash, all at once. IMG does not get saved to disk. 243 | Slower: Download and unzip, then flash. Raw IMG file remains.' | yad --list --separator='\n' \ 244 | --center --width=570 --height=50 --no-click --no-headers --text="Select a download program:" \ 245 | --column=Name --title="Download Options" --window-icon="${DIRECTORY}/icons/logo-64.png" \ 246 | --button='Cancel'!"${DIRECTORY}/icons/exit.png":1 \ 247 | --button='Flash'!"${DIRECTORY}/icons/burn.png":0 \ 248 | 2>/dev/null)" 249 | button=$? 250 | if [ ! -z "$output" ];then 251 | output="$(echo "$output" | awk '{print $1}')" #get only first word 252 | output="${output::-1}" #remove colon from end of first word 253 | fi 254 | 255 | #print values to terminal 256 | echo "Output: ${output}EOO" 257 | echo "Button: ${button}EOB" 258 | 259 | if [ $button -eq 252 ] || [ $button -eq 143 ] || [ $button -eq 1 ];then 260 | #if window manager x was pressed 261 | # or if yad was killed 262 | # or if cancel was pressed 263 | #then exit the program 264 | exit 0 265 | fi 266 | 267 | dlurl="$(echo "$matchmirror" | awk -F '\t' '{print $3}')" 268 | dlname="$(echo "$matchmirror" | awk -F '\t' '{print $1}')" 269 | dlsize="$(wget -qO- "$dlurl" | dd bs=1M count=1 2>/dev/null | busybox unzip -lq - 2>/dev/null | sed -n '3p' | awk '{print $1}')" 270 | dlfilename="$(wget -qO- "$dlurl" | dd bs=1M count=1 2>/dev/null | busybox unzip -lq - 2>/dev/null | sed -n '3p' | awk '{print $4}')" 271 | 272 | echo "Download name: $dlname" 273 | echo "Download size: $dlsize" 274 | echo "Download url: $dlurl" 275 | echo "Download filename: $dlfilename" 276 | 277 | 278 | if [ -z "$dlsize" ] || [ -z "$dlfilename" ];then 279 | errorbox "Could not connect to server. Likely no Internet connection." fatal 280 | fi 281 | 282 | if [ "$output" = Slower ];then 283 | #SLOWER 284 | startseconds=$(cat /proc/uptime | awk '{print $1}' | awk '{print int($1+0.5)}') 285 | 286 | getdevname 287 | umountsubdevs 288 | 289 | (echo "0" 290 | #download-img is a temporary folder the script downloads its disk image to 291 | #the script has to get the full filename 292 | #temporarily storing in this folder prevents issues if there is already a img in the home directory 293 | echo "# Removing residual folder ${HOME}/download-img" 294 | rm -rf "${HOME}/download-img" 295 | 296 | mkdir download-img 297 | cd download-img 298 | echo 10 299 | 300 | curbytes=1 301 | while true;do 302 | curbytes="$(ls -al * 2>/dev/null | awk '{print $5}')" 303 | #echo $curbytes 304 | progress="$((curbytes/(dlsize/100)))" 305 | if [ $progress -gt 98 ];then 306 | progress=98 307 | fi 308 | echo $progress 309 | echo "# Downloading $FROM: $(numfmt --to=iec --suffix=B --padding=7 $curbytes)" 310 | sleep 0.1 311 | done & 312 | childpid=$! 313 | trap "kill $childpid" EXIT 314 | 315 | #add first 10 mb to cache. This speeds the download process. 316 | wget -qO- "$dlurl" | "${DIRECTORY}/functions/buffer" | busybox unzip - -p | dd ibs=10M obs=10M | dd bs=10M count=1 of=/dev/null 317 | trap "kill $!" EXIT 318 | 319 | wget -qO- "$dlurl" | "${DIRECTORY}/functions/buffer" | busybox unzip - -p | "${DIRECTORY}/functions/buffer" | dd of="${HOME}/download-img/ppt-dl-${dlfilename}" bs=1M 320 | trap "kill $!" EXIT 321 | kill "$childpid" 322 | 323 | #STAGE 2 324 | DL_IMG_LOC="$(find "$(pwd)" -type f)" 325 | cd "$HOME" 326 | 327 | startwriteblks="$(cat /proc/diskstats | grep $devname | head -n1 | awk '{print $10}')" 328 | sleep 1 329 | 330 | while true;do 331 | curwriteblks="$(cat /proc/diskstats | grep $devname | head -n1 | awk '{print $10}')" 332 | sleep 0.1 333 | # bytes written 334 | progress="$(((curwriteblks-startwriteblks)*512*100/dlsize))" 335 | 336 | #prevent progress from exceeding 98 337 | if [ $progress -gt 98 ];then 338 | progress=98 339 | fi 340 | echo $progress 341 | echo "# Flashing $TO: $(numfmt --to=iec --suffix=B $(((curwriteblks-startwriteblks)*512)))" 342 | done & 343 | childpid=$! 344 | trap "kill $childpid" EXIT 345 | 346 | dd if="$DL_IMG_LOC" | "${DIRECTORY}/functions/buffer" | sudo dd of="$TO" bs=10M 347 | trap "kill $!" EXIT 348 | kill "$childpid" 349 | 350 | echo "# Flash finished. Cleaning up..." 351 | echo 90 352 | mv "$DL_IMG_LOC" "$HOME" 353 | rm -rf "${HOME}/download-img" 354 | 355 | #get new img location and add it to imglist 356 | filename="$(echo "$DL_IMG_LOC" | awk -F '/' 'NF>1{print $NF}')" 357 | echo "${HOME}/${filename}" >> "${DIRECTORY}/data/imglist" 358 | 359 | ) | 360 | yad --progress --auto-kill --auto-close --title="Flash" --center \ 361 | --window-icon="${DIRECTORY}/icons/logo-64.png" \ 362 | --text="Downloading $dlname then flashing $TO ..." \ 363 | --button=Cancel!"${DIRECTORY}/icons/exit.png":1 2>/dev/null 364 | 365 | #END OF SLOWER DOWNLOAD SECTION 366 | else 367 | #FASTER 368 | 369 | getdevname 370 | umountsubdevs 371 | 372 | #if no action selected, Faster download mode is assumed. 373 | 374 | #get seconds of uptime at start to calculate time taken to flash 375 | startseconds=$(cat /proc/uptime | awk '{print $1}' | awk '{print int($1+0.5)}') 376 | 377 | echo "from: $dlurl" 378 | echo "to: $TO" 379 | 380 | #add first 10 mb to cache. This speeds the download process. 381 | wget -qO- "$dlurl" | "${DIRECTORY}/functions/buffer" | busybox unzip - -p | dd ibs=10M obs=10M | dd bs=10M count=1 of=/dev/null 382 | 383 | wget -qO- "$dlurl" | "${DIRECTORY}/functions/buffer" | busybox unzip - -p | "${DIRECTORY}/functions/buffer" | sudo dd of="$TO" obs=1M & 384 | #kill the above command on exit 385 | trap "kill $!" EXIT 386 | 387 | (echo 0 388 | #get dlsize of img 389 | startwriteblks="$(cat /proc/diskstats | grep $devname | head -n1 | awk '{print $10}')" 390 | 391 | sleep 1 392 | 393 | #repeat until dd closed 394 | while ps -C dd >/dev/null ;do 395 | curwriteblks="$(cat /proc/diskstats | grep $devname | head -n1 | awk '{print $10}')" 396 | sleep 0.1 397 | # bytes written 398 | progress="$(((curwriteblks-startwriteblks)*512*100/dlsize))" 399 | 400 | #prevent progress from exceeding 98 401 | if [ $progress -gt 98 ];then 402 | progress=98 403 | fi 404 | echo $progress 405 | echo "# $(numfmt --to=iec --suffix=B $(((curwriteblks-startwriteblks)*512)))" 406 | done 407 | echo "# done" 408 | echo 100 409 | 410 | )| yad --progress --title=Flashing --window-icon="${DIRECTORY}/icons/logo-64.png" \ 411 | --width=300 --height=100 --center --auto-close \ 412 | --text="Flashing $TO directly from Internet..." \ 413 | --button=Cancel!"${DIRECTORY}/icons/exit.png":1 2>/dev/null 414 | 415 | #END OF FASTER DOWNLOAD SECTION 416 | fi 417 | #this section is run after download flash modes. Never runs after a offline flash. 418 | 419 | sudo killall wget 420 | 421 | elif [[ "$FROM" == *".img" ]] && [ -f "$FROM" ];then 422 | #IMG MODE 423 | #not download mode 424 | 425 | #get img location and add it to imglist 426 | echo "$FROM" >> "${DIRECTORY}/data/imglist" 427 | 428 | getdevname 429 | umountsubdevs 430 | 431 | #get seconds of uptime at start to calculate time taken to flash 432 | startseconds=$(cat /proc/uptime | awk '{print $1}' | awk '{print int($1+0.5)}') 433 | 434 | dd if="$FROM" | "${DIRECTORY}/functions/buffer" | sudo dd of="$TO" bs=10M & 435 | #kill the above command on exit 436 | trap "kill $!" EXIT 437 | 438 | (echo "# Flashing from IMG file..." 439 | 440 | #get size of img 441 | dlsize="$(ls -l "$FROM" | awk '{print $5}')" 442 | 443 | startwriteblks="$(cat /proc/diskstats | grep $devname | head -n1 | awk '{print $10}')" 444 | 445 | sleep 1 446 | 447 | #repeat until dd closed 448 | while ps -C dd >/dev/null ;do 449 | curwriteblks="$(cat /proc/diskstats | grep $devname | head -n1 | awk '{print $10}')" 450 | sleep 0.1 451 | # bytes written 452 | progress="$(((curwriteblks-startwriteblks)*512*100/dlsize))" 453 | 454 | #prevent progress from exceeding 98 455 | if [ $progress -gt 98 ];then 456 | progress=98 457 | fi 458 | echo $progress 459 | echo "# $(numfmt --to=iec --suffix=B $(((curwriteblks-startwriteblks)*512)))" 460 | done 461 | echo 100 462 | )| yad --progress \ 463 | --title=Flashing --window-icon="${DIRECTORY}/icons/logo-64.png" \ 464 | --text="Flashing from ${FROM} to ${TO}." \ 465 | --width=300 --height=100 --center --auto-close \ 466 | --button=Cancel!"${DIRECTORY}/icons/exit.png"!:1 2>/dev/null 467 | elif [[ "$FROM" == *".zip" ]] && [ -f "$FROM" ];then 468 | #ZIP MODE 469 | #not download or img mode 470 | 471 | #get zip location and add it to ziplist 472 | echo "$FROM" >> "${DIRECTORY}/data/ziplist" 473 | 474 | getdevname 475 | umountsubdevs 476 | 477 | #get seconds of uptime at start to calculate time taken to flash 478 | startseconds=$(cat /proc/uptime | awk '{print $1}' | awk '{print int($1+0.5)}') 479 | 480 | busybox unzip "$FROM" -p | "${DIRECTORY}/functions/buffer" | sudo dd of="$TO" bs=10M & 481 | #kill the above command on exit 482 | trap "kill $!" EXIT 483 | 484 | (echo "# Flashing from ZIP file..." 485 | 486 | #get size of img 487 | dlsize="$(busybox unzip -lq "$FROM" 2>/dev/null | sed -n '3p' | awk '{print $1}')" 488 | 489 | startwriteblks="$(cat /proc/diskstats | grep $devname | head -n1 | awk '{print $10}')" 490 | 491 | sleep 1 492 | 493 | #repeat until dd closed 494 | while ps -C dd >/dev/null ;do 495 | curwriteblks="$(cat /proc/diskstats | grep $devname | head -n1 | awk '{print $10}')" 496 | sleep 0.1 497 | # bytes written 498 | progress="$(((curwriteblks-startwriteblks)*512*100/dlsize))" 499 | 500 | #prevent progress from exceeding 98 501 | if [ $progress -gt 98 ];then 502 | progress=98 503 | fi 504 | echo $progress 505 | echo "# $(numfmt --to=iec --suffix=B $(((curwriteblks-startwriteblks)*512)))" 506 | done 507 | echo 100 508 | )| yad --progress \ 509 | --title=Flashing --window-icon="${DIRECTORY}/icons/logo-64.png" \ 510 | --text="Flashing from ${FROM} to ${TO}." \ 511 | --width=300 --height=100 --center --auto-close --auto-kill 2>/dev/null 512 | else 513 | errorbox "File not found." fatal 514 | fi 515 | 516 | #ensure all data is finished writing 517 | sync 518 | 519 | #get seconds of uptime at end to calculate time taken to flash 520 | endseconds=$(cat /proc/uptime | awk '{print $1}' | awk '{print int($1+0.5)}') 521 | 522 | #kill dd 2 different ways to make sure actually killed. 523 | sudo killall dd 2>/dev/null 524 | sudo kill -9 $(ps -e|grep ' dd' | awk '{print $1}') 2>/dev/null 525 | 526 | yad --wrap --text="Flashing complete.\nThat took $((endseconds-startseconds)) seconds." \ 527 | --title=Flashing --window-icon="${DIRECTORY}/icons/logo-64.png" --width=300 --height=100 \ 528 | --center --button=Close!"${DIRECTORY}/icons/exit.png":0 2>/dev/null 529 | 530 | #kill any accidental subprocesses 531 | sudo killall flash 532 | -------------------------------------------------------------------------------- /functions/advmount: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIRECTORY="$(dirname "$(dirname $0)")" 3 | 4 | 5 | 6 | #variable 1 is img file 7 | #if left blank prompt user 8 | if [ -z $1 ];then 9 | 10 | #parse imglist 11 | imglist="$(${DIRECTORY}/functions/imglist-parser || errorbox 'Failed to parse imglist!')" 12 | 13 | IMG="$(yad --title='Advanced Mount' --center --window-icon="${DIRECTORY}/icons/logo-64.png" \ 14 | --form --separator='\n' --width=500 --height=150\ 15 | --field="Paste path::CBE" "$imglist" \ 16 | --image="${DIRECTORY}/icons/img-48.png" --image-on-top 2>/dev/null \ 17 | --button="Close!${DIRECTORY}/icons/exit.png:1" \ 18 | --button="Next!${DIRECTORY}/icons/forward.png:0")" 19 | button=$? 20 | if [ $button -eq 1 ] || [ $button -eq 252 ] || [ $button -eq 143 ];then 21 | exit 0 22 | fi 23 | echo "You chose: ${IMG}" 24 | 25 | if [[ "$IMG" == *".img" ]] && [ -f "$IMG" ];then #ensure disk image exists 26 | echo "Disk image detected." 27 | echo "$IMG" >> "${DIRECTORY}/data/imglist" 28 | else 29 | echo "No such file or directory." 30 | yad --title='Advanced Mount' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 31 | --text="Could not locate a disk image at the location you specified." \ 32 | --center --text-align=center \ 33 | --button="Close!${DIRECTORY}/icons/exit.png:1" 2>/dev/null 34 | exit 1 35 | fi 36 | else 37 | IMG="$1" 38 | fi 39 | 40 | #trap cleanup EXIT 41 | 42 | #check if loop device already exists 43 | available_loops="$(sudo losetup --list -nO back-file,name)" 44 | 45 | numlines=$(echo "$available_loops" | wc -l | awk '{print $1}') 46 | LOOP='' 47 | LOOP="$(sudo losetup --list -nO back-file,name | grep "$IMG" | tr ' ' '\n' | grep 'loop' | sort)" 48 | 49 | echo "Loop: ${LOOP}EOL" 50 | 51 | #if multiple loop devices connected to img 52 | numlines=$(echo "$LOOP" | wc -l | awk '{print $1}') 53 | if [ $numlines -gt 1 ];then 54 | repeat=1 55 | else 56 | repeat=0 57 | fi 58 | 59 | 60 | #how many loop devices connected to img 61 | numlines=$(echo "$LOOP" | wc -l | awk '{print $1}') 62 | 63 | while [ $numlines -gt 1 ];do 64 | echo "Multiple loopback devices linked to this img." 65 | #refresh loop file 66 | LOOP="$(sudo losetup --list -nO back-file,name | grep "$IMG" | tr ' ' '\n' | grep 'loop' | sort)" 67 | 68 | LOOP="$(echo -e "$LOOP" | yad --list --separator='\n' \ 69 | --title="Advanced Mount" --center --window-icon="${DIRECTORY}/icons/logo-64.png" \ 70 | --column="Device" --print-column=1 \ 71 | --text="There are multiple loop devices associated\nwith $IMG\nWhich one?" \ 72 | --button=Select!"${DIRECTORY}/icons/forward.png:2" \ 73 | --button=Detach!"${DIRECTORY}/icons/trash.png:0" \ 74 | 2>/dev/null)" 75 | button=$? #get exit code to determine which button was pressed 76 | echo "Output: ${LOOP}EOO" 77 | if [ $button -eq 252 ];then #if window manager x was pressed 78 | exit 0 79 | fi 80 | case $button in 81 | 1) 82 | echo "Error" 83 | exit 1 84 | ;; 85 | 86 | 2) 87 | echo "Select" 88 | numlines=1 #escape loop 89 | ;; 90 | 0) 91 | echo "Detach" 92 | sudo umount $LOOP 93 | #get subdevices 94 | partitions="$(lsblk $LOOP -no PATH | tail -n +2 | tr '\n' ' ')" 95 | #echo "Subdevices: ${partitions}" 96 | #unmount subdevices 97 | sudo umount $partitions 98 | #delete block device 99 | sudo losetup -d "$LOOP" 100 | 101 | #refresh numlines variable 102 | LOOP="$(sudo losetup --list -nO back-file,name | grep "$IMG" | tr ' ' '\n' | grep 'loop' | sort)" 103 | numlines=$(echo "$LOOP" | wc -l | awk '{print $1}') 104 | echo "Numlines is now $numlines" 105 | ;; 106 | esac 107 | done 108 | 109 | #if loop device does not already exist, create one 110 | if [ -z $LOOP ];then 111 | echo "creating loop device" 112 | LOOP="$(sudo losetup -fP --show "$IMG")" || exit 1 113 | fi 114 | 115 | echo "Loop device: $LOOP" 116 | partitions="$(lsblk $LOOP -no PATH | tail -n +2)" 117 | numlines=$(echo "$partitions" | wc -l | awk '{print $1}') 118 | #echo "partitions: ${partitions}EOO" 119 | 120 | #sudo mount /dev/loop0p2 $mntpnt 121 | 122 | 123 | 124 | #while loop begins 125 | while true; do 126 | 127 | 128 | #get subdevices and generate columns 129 | i=1 130 | #clear all variables 131 | label='' 132 | part_icon='' 133 | part_mnt='' 134 | part_total='' 135 | part_used='' 136 | tooltips='' 137 | 138 | while [ $i -le $numlines ]; do #repeat until last line is reached 139 | echo '' 140 | echo $i 141 | cur_dev=$(echo "$partitions" | sed -n "${i}p") 142 | echo $cur_dev 143 | 144 | #add mountpoint to part_mnt variable 145 | part_mnt="$part_mnt\n$(sudo findmnt -nmo TARGET "$cur_dev")" 146 | 147 | 148 | label="$label\n$(lsblk -nmo LABEL "$cur_dev")" 149 | 150 | #generate icon for each line of list 151 | if [ -z $(sudo findmnt -nmo TARGET "$cur_dev") ];then 152 | #if not mounted 153 | #display unmounted icon 154 | part_icon="${part_icon}\n${DIRECTORY}/icons/unmounted.png" 155 | 156 | #blank in total size 157 | part_total="${part_total}\n" 158 | part_used="${part_used}\n" 159 | tooltips="${tooltips}\nMounted" 160 | 161 | #add tooltip 162 | tooltips="${tooltips}\nUnmounted" 163 | else 164 | #if mounted 165 | #display mounted icon 166 | part_icon="${part_icon}\n${DIRECTORY}/icons/mounted.png" 167 | 168 | #get used space of partition 169 | part_used="${part_used}\n$(echo $((1024*$(df --output=used "$cur_dev" | sed -n 2p | tr -d '\n' | tr -d ' '))) | numfmt --to=iec 2>/dev/null)" 170 | 171 | #add tooltip 172 | tooltips="${tooltips}\nMounted" 173 | fi 174 | 175 | 176 | i=$((i+1)) #check the next line 177 | done 178 | 179 | #trim off first line break escape code 180 | part_mnt=${part_mnt:2} 181 | #convert line break escape codes to real line breaks 182 | part_mnt="$(echo -e "$part_mnt")" 183 | 184 | #trim off first line break escape code 185 | part_icon=${part_icon:2} 186 | #convert line break escape codes to real line breaks 187 | part_icon="$(echo -e "$part_icon")" 188 | 189 | #trim off first line break escape code 190 | part_total=${part_total:2} 191 | #convert line break escape codes to real line breaks 192 | part_total="$(echo -e "$part_total")" 193 | echo "part total: ${part_total}EOP" 194 | 195 | #trim off first line break escape code 196 | part_used=${part_used:2} 197 | #convert line break escape codes to real line breaks 198 | part_used="$(echo -e "$part_used")" 199 | echo "part used: ${part_used}EOP" 200 | 201 | #trim off first line break escape code 202 | label=${label:2} 203 | #convert line break escape codes to real line breaks 204 | label="$(echo -e "$label")" 205 | 206 | losetup --list -nO BACK-FILE "$LOOP" 207 | #exit 0 208 | 209 | #echo "Numlines: ${numlines}" 210 | #echo "partitions: ${partitions}EOO" 211 | #echo "mounted: ${part_mnt}EOM" 212 | 213 | parted_out="$(sudo parted "$LOOP" print -m)" 214 | #part_nums="$(echo $parted_out | tr ';' '\n' | tail -n +3 | tr -d ' ' | awk -F ":" '{print $1}')" 215 | part_type="$(echo $parted_out | tr ';' '\n' | tail -n +3 | tr -d ' ' | awk -F ":" '{print $5}')" 216 | part_total="$(echo $parted_out | tr ';' '\n' | tail -n +3 | tr -d ' ' | awk -F ":" '{print $4}' | tr -d 'B' | numfmt --to=si --from=auto 2>/dev/null)" 217 | 218 | part_list="$(paste -d '\n' <(echo "$part_icon") <(echo "$partitions") <(echo "$part_type") <(echo "$label") <(echo "$part_total") <(echo "$part_used") <(echo "$part_mnt"))" 219 | #echo "$part_list" 220 | 221 | output="$(echo -e "$part_list" | yad --list --no-click --separator='\n' \ 222 | --title="Advanced Mount" --width=600 --height=260 --center --window-icon="${DIRECTORY}/icons/logo-64.png" \ 223 | --column=:IMG --column="Partition" --column="FS Type" --column="Label" --column="Size" --column="Used" --column="Mountpoint" \ 224 | --text="Partitions within $IMG" \ 225 | --print-column=2 \ 226 | --button=Detach!"${DIRECTORY}/icons/trash.png"!"All done? Click this to unmount everything and detach $LOOP.:4" \ 227 | --button=Unmount!"${DIRECTORY}/icons/usb-unmount.png"!'Safely eject to avoid corruption when you unplug it.:2' \ 228 | --button=Mount!"${DIRECTORY}/icons/usb-mount.png"!'This lets you view the files.:0' \ 229 | 2>/dev/null)" 230 | 231 | button=$? #get exit code to determine which button was pressed 232 | echo "Button: ${button}" 233 | if [ $button -eq 252 ];then #if window manager x was pressed 234 | exit 0 235 | fi 236 | 237 | echo "Output: ${output}EOO" 238 | 239 | case $button in 240 | 241 | 1) 242 | echo "Error" 243 | exit 1 244 | ;; 245 | 246 | 0) 247 | echo "Mount" 248 | if [ -z $output ];then 249 | yad --title='Advanced Mount' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 250 | --text="Mind reading is not supported.\n\n(It looks like you did not select any devices.)\nTry again?" \ 251 | --center --text-align=center \ 252 | --button="No!${DIRECTORY}/icons/exit.png:1" \ 253 | --button="Yes!${DIRECTORY}/icons/forward.png:0" 2>/dev/null || exit 0 254 | else 255 | mntpnt=$(yad --title='Advanced Mount' --width=300 --window-icon="${DIRECTORY}/icons/logo-64.png" \ 256 | --center --text="Mount to:" --entry --entry-text="$(cat ${DIRECTORY}/data/mountpoint || echo /media/pi/pi-power-tools)" \ 257 | --button="Cancel!${DIRECTORY}/icons/exit.png:1" --button="OK!${DIRECTORY}/icons/forward.png:0" 2>/dev/null) 258 | 259 | #only try to mount if ok button was pressed 260 | if [ $? -eq 0 ];then 261 | sudo mkdir "$mntpnt" 262 | errorcode=$(sudo mount $output "$mntpnt" 2>&1) 263 | if [ ! $? -eq 0 ];then 264 | yad --title='Advanced Mount' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 265 | --text="Could not mount successfully.\nError is:\n$errorcode" \ 266 | --center --text-align=center \ 267 | --button="OK:0" 2>/dev/null 268 | fi 269 | fi 270 | fi 271 | ;; 272 | 273 | 2) 274 | echo "Unmount" 275 | if [ -z $output ];then 276 | yad --title='Advanced Mount' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 277 | --text="Mind reading is not supported.\n\n(It looks like you did not select any devices.)\nTry again?" \ 278 | --center --text-align=center \ 279 | --button="No!${DIRECTORY}/icons/exit.png:1" \ 280 | --button="Yes!${DIRECTORY}/icons/forward.png:0" 2>/dev/null || exit 0 281 | else 282 | errorcode=$(sudo umount "$output" 2>&1) 283 | if [ ! $? -eq 0 ];then 284 | yad --title='Advanced Mount' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 285 | --text="Could not unmount successfully.\nError is:\n$errorcode" \ 286 | --center --text-align=center \ 287 | --button="OK:0" 2>/dev/null 288 | fi 289 | fi 290 | ;; 291 | 4) 292 | echo "Detach" 293 | sudo umount $LOOP 294 | #get subdevices 295 | partitions="$(lsblk $LOOP -no PATH | tail -n +2 | tr '\n' ' ')" 296 | echo "Subdevices: ${partitions}" 297 | #unmount subdevices 298 | sudo umount $partitions 299 | #delete loopback device 300 | sudo losetup -d "$LOOP" 301 | exit 0 302 | ;; 303 | esac 304 | done 305 | 306 | 307 | 308 | 309 | 310 | 311 | -------------------------------------------------------------------------------- /functions/buffer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/functions/buffer -------------------------------------------------------------------------------- /functions/edit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #$1 is path to img file 3 | #img path is NOT checked to ensure it exists. 4 | 5 | errorbox(){ 6 | #$1 is message 7 | #$2 is type: fatal or left blank 8 | if [ -z "$2" ];then 9 | yad --title='Pi Power Tools' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 10 | --text="Oh no!\n\n$1\nTry again?" \ 11 | --center --width=300 --text-align=center \ 12 | --button="OK!${DIRECTORY}/icons/forward.png:0" 2>/dev/null 13 | elif [ "$2" = 'fatal' ];then 14 | yad --title='Pi Power Tools' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 15 | --text="Fatal error:\n$1\nClosing in 30 seconds." \ 16 | --center --width=300 --text-align=center \ 17 | --timeout=30 --timeout-indicator=bottom \ 18 | --button="Close!${DIRECTORY}/icons/exit.png:0" 2>/dev/null 19 | exit 1 20 | else 21 | errorbox 'errorbox error' fatal 22 | fi 23 | } 24 | 25 | DIRECTORY="$(dirname "$(dirname $0)")" 26 | 27 | ${DIRECTORY}/installgui "gparted" &>/dev/null || exit 0 28 | 29 | LOOP="$(sudo losetup -fP --show "$1")" #create loopback device 30 | 31 | echo $LOOP 32 | if [ -z $LOOP ];then 33 | errorbox "Could not create loopback device." fatal 34 | fi 35 | sudo killall gparted 36 | sudo gparted "$LOOP" 37 | #when gparted closed 38 | sudo losetup -d "$LOOP" &>/dev/null #delete loopback device 39 | -------------------------------------------------------------------------------- /functions/image-shrink: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkloop() 4 | { 5 | LOOP="$(losetup -f --show -P "${IMGFILE}")" 6 | if [ $? -ne 0 ]; then 7 | errexit "Unable to create loop device" 8 | fi 9 | } 10 | 11 | rmloop() 12 | { 13 | losetup -d "${LOOP}" 14 | } 15 | 16 | fsckerr() 17 | { 18 | rmloop 19 | errexit "Filesystem appears corrupted "$1" resize2fs" 20 | } 21 | 22 | errexit() 23 | { 24 | echo "" 25 | echo -e "\e[91m$1\e[39m" 26 | echo "" 27 | exit 1 28 | } 29 | 30 | if [ $(id -u) -ne 0 ]; then 31 | errexit "$0 must be run as root user" 32 | fi 33 | PGMNAME="$(basename $0)" 34 | for PID in $(pidof -x -o %PPID "${PGMNAME}"); do 35 | if [ ${PID} -ne $$ ]; then 36 | errexit "${PGMNAME} is already running" 37 | fi 38 | done 39 | gdisk -l "${DEVICE}" &> /dev/null 40 | if [ $? -eq 127 ]; then 41 | echo "" 42 | echo "gdisk not installed. Installing gdisk" 43 | echo "" 44 | apt-get update 45 | apt-get install gdisk 46 | fi 47 | IMGFILE="$1" 48 | if [ "${IMGFILE}" = "" ]; then 49 | errexit "Usage: $0 imagefile [Additional MB]" 50 | fi 51 | if [ ! -f "${IMGFILE}" ] || [ ! -s "${IMGFILE}" ]; then 52 | errexit "${IMGFILE} is missing or empty" 53 | fi 54 | mkloop 55 | FS_TYPE=$(blkid "${LOOP}p2" | sed -n 's|^.*TYPE="\(\S\+\)".*|\1|p') 56 | rmloop 57 | if [ "${FS_TYPE}" = "f2fs" ]; then 58 | errexit "Cannot shrink F2FS filesystem" 59 | fi 60 | answer="$2" 61 | if [[ ! "${answer}" =~ ^[0-9]+$ ]]; then 62 | errexit "You must specify additional MB." 63 | fi 64 | while [ "${answer:0:1}" = "0" ]; do 65 | answer="${answer:1}" 66 | done 67 | if [ ${#answer} -eq 0 ]; then 68 | answer=0 69 | fi 70 | ADDMB=${answer} 71 | echo "" 72 | INFO="$(sfdisk -d "${IMGFILE}")" 73 | BOOTBEG=$(sed -n "s|^${IMGFILE}1.*start=\s*\([0-9]\+\).*$|\1|p" <<< "${INFO}") 74 | BOOTEND=$((${BOOTBEG} + $(sed -n "s|^${IMGFILE}1.*size=\s*\([0-9]\+\).*$|\1|p" <<< "${INFO}") - 1)) 75 | ROOTBEG=$(sed -n "s|^${IMGFILE}2.*start=\s*\([0-9]\+\).*$|\1|p" <<< "${INFO}") 76 | PARTUUID_1="$(sed -n "s|^${IMGFILE}1.*uuid=\(\S\+\).*$|\1|p" <<< "${INFO}")" 77 | PARTUUID_2="$(sed -n "s|^${IMGFILE}2.*uuid=\(\S\+\).*$|\1|p" <<< "${INFO}")" 78 | PTUUID="$(sed -n "s|^label-id: \(\S\+\).*$|\1|p" <<< "${INFO}")" 79 | PTTYPE="$(sed -n "s|^label: \(\S\+\).*$|\1|p" <<< "${INFO}")" 80 | if [[ "${PTTYPE}" != "dos" && "${PTTYPE}" != "gpt" ]]; then 81 | errexit "Unsupported partition table type: ${PTTYPE}" 82 | fi 83 | mkloop 84 | e2fsck -f -n "${LOOP}p2" 85 | if [ $? -ne 0 ]; then 86 | fsckerr "before" 87 | fi 88 | echo "" 89 | resize2fs -f -M "${LOOP}p2" 90 | resize2fs -f -M "${LOOP}p2" 91 | resize2fs -f -M "${LOOP}p2" 92 | e2fsck -f -n "${LOOP}p2" 93 | if [ $? -ne 0 ]; then 94 | fsckerr "after" 95 | fi 96 | INFO="$(tune2fs -l "${LOOP}p2" 2>/dev/null)" 97 | rmloop 98 | NEWSIZE=$(sed -n 's|^Block count:\s*\(.*\)|\1|p' <<< "${INFO}") 99 | BLKSIZE=$(sed -n 's|^Block size:\s*\(.*\)|\1|p' <<< "${INFO}") 100 | NEWEND=$((${ROOTBEG} + (${NEWSIZE} * (${BLKSIZE} / 512)) + ((${ADDMB} * 1024 * 1024) / 512) - 1)) 101 | if [ "${PTTYPE}" = "gpt" ]; then 102 | ((NEWEND += 33)) 103 | fi 104 | truncate -s $(((${NEWEND} + 1) * 512)) "${IMGFILE}" 105 | if [ "${PTTYPE}" = "dos" ]; then 106 | sfdisk --delete "${IMGFILE}" 2 > /dev/null 107 | echo "${ROOTBEG},+" | sfdisk -N2 "${IMGFILE}" &> /dev/null 108 | else 109 | sgdisk -Z "${IMGFILE}" &> /dev/null 110 | sgdisk -n 1:${BOOTBEG}:${BOOTEND} "${IMGFILE}" > /dev/null 111 | sgdisk -t 1:0700 "${IMGFILE}" > /dev/null 112 | sgdisk -n 2:${ROOTBEG}:0 "${IMGFILE}" > /dev/null 113 | sgdisk -t 2:8300 "${IMGFILE}" > /dev/null 114 | sgdisk -u 1:"${PARTUUID_1}" "${IMGFILE}" > /dev/null 115 | sgdisk -u 2:"${PARTUUID_2}" "${IMGFILE}" > /dev/null 116 | sgdisk -U "${PTUUID}" "${IMGFILE}" > /dev/null 117 | gdisk "${IMGFILE}" < /dev/null 118 | r 119 | h 120 | 1 121 | n 122 | 0c 123 | n 124 | n 125 | w 126 | y 127 | EOF 128 | fi 129 | if [ ${ADDMB} -ne 0 ]; then 130 | echo "" 131 | mkloop 132 | e2fsck -f -n "${LOOP}p2" 133 | if [ $? -ne 0 ]; then 134 | fsckerr "before" 135 | fi 136 | echo "" 137 | resize2fs -f "${LOOP}p2" 138 | e2fsck -f -n "${LOOP}p2" 139 | if [ $? -ne 0 ]; then 140 | fsckerr "after" 141 | fi 142 | rmloop 143 | fi 144 | echo "" 145 | -------------------------------------------------------------------------------- /functions/imglist-parser: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #this short script checks the data/imglist file and ensures there are no duplicate lines or nonexistent files. 3 | 4 | DIRECTORY="$(dirname "$(dirname $0)")" 5 | 6 | echo "$(find "$HOME" -maxdepth 1 -name "*.img" )" >> "${DIRECTORY}/data/imglist" 7 | echo "$(find "$HOME/Downloads" -maxdepth 1 -name "*.img")" >> "${DIRECTORY}/data/imglist" 8 | 9 | mkdir -p "${DIRECTORY}/data" 10 | 11 | #if imglist exists, ensure all lines contain valid files 12 | if [ -f "${DIRECTORY}/data/imglist" ];then #if imglist exists 13 | #number of lines in file 14 | numlines=$(wc -l "${DIRECTORY}/data/imglist" | awk '{ print $1 }') 15 | #echo $numlines 16 | if [ $numlines -gt 0 ];then #if imglist contains any lines 17 | echo "$(sort "${DIRECTORY}/data/imglist" | uniq)" > "${DIRECTORY}/data/imglist" #remove duplicate lines 18 | i=1 19 | while [ $i -le $numlines ]; do #repeat until all lines are checked 20 | line=$(sed -n "${i}p" < "${DIRECTORY}/data/imglist") #read that line of text file 21 | #echo $line 22 | if [ ! -f $line ];then #if line is file not found 23 | #echo "not exist!" 24 | sed -i "${i}d" "${DIRECTORY}/data/imglist" #delete the line 25 | fi 26 | i=$((i+1)) #increase line number by 1 to check the next one 27 | done 28 | imglist="$(cat ${DIRECTORY}/data/imglist | tr '\n' '!')" 29 | fi 30 | else 31 | #create file 32 | echo '' > "${DIRECTORY}/data/imglist" 33 | exit 1 34 | fi 35 | if [ ! -z "$imglist" ];then 36 | imglist="${imglist::-1}" 37 | fi 38 | echo "$imglist" #this is the output of the script 39 | -------------------------------------------------------------------------------- /functions/milliways-image-backup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from https://raspberrypi.stackexchange.com/questions/103990/how-to-backup-shrink-the-image-of-my-working-raspbian-sd-card/103991#103991 3 | # Original https://raspberrypi.org/forums/viewtopic.php?p=1528736 4 | # 2019-09-26 Modified to set size of boot sector 5 | 6 | trap '{ stty sane; echo ""; errexit "Aborted"; }' SIGINT SIGTERM 7 | 8 | ADDBLK=0 9 | 10 | # Set BOOT_SIZE_MB to the Desired boot sector size (in MB) - should be multiple of 4MB 11 | BOOT_SIZE_MB=256 12 | BOOTSIZEM=$BOOT_SIZE_MB'M' 13 | 14 | BOOTBEG=8192 15 | BOOT_SIZE="$((BOOT_SIZE_MB * 1024 * 1024))" 16 | ROUND_SIZE="$((4 * 1024 * 1024))" 17 | # Ensure root sector starts on an Erase Block Boundary (4MB) 18 | ROOTBEG=$(((BOOT_SIZE + ROUND_SIZE -1) / ROUND_SIZE * ROUND_SIZE / 512 + BOOTBEG)) 19 | 20 | mntpnt="/tmp/img-backup-mnt" 21 | 22 | ONEMB=$((1024 * 1024)) 23 | 24 | rsyncargs="$2" 25 | 26 | # create BOOT loop device 27 | mkloop1() 28 | { 29 | local INFO1="" 30 | local SIZE1=0 31 | local START1=0 32 | 33 | sync 34 | INFO1="$(sfdisk -d "${img}")" 35 | START1=$(grep type=c <<< "${INFO1}" | sed -n 's|^.*start=\s\+\([0-9]\+\).*$|\1|p') 36 | SIZE1=$(grep type=c <<< "${INFO1}" | sed -n 's|^.*size=\s\+\([0-9]\+\).*$|\1|p') 37 | LOOP1="$(losetup -f --show -o $((${START1} * 512)) --sizelimit $((${SIZE1} * 512)) "${img}")" 38 | if [ $? -ne 0 ]; then 39 | errexit "Unable to create BOOT loop device" 40 | fi 41 | } 42 | 43 | rmloop1() 44 | { 45 | if [ "${LOOP1}" != "" ]; then 46 | sync 47 | losetup -d "${LOOP1}" 48 | LOOP1="" 49 | fi 50 | } 51 | 52 | # create ROOT loop device 53 | mkloop2() 54 | { 55 | local INFO2="" 56 | local SIZE2=0 57 | local START2=0 58 | 59 | sync 60 | INFO2="$(sfdisk -d "${img}")" 61 | START2=$(grep type=83 <<< "${INFO2}" | sed -n 's|^.*start=\s\+\([0-9]\+\).*$|\1|p') 62 | SIZE2=$(grep type=83 <<< "${INFO2}" | sed -n 's|^.*size=\s\+\([0-9]\+\).*$|\1|p') 63 | LOOP2="$(losetup -f --show -o $((${START2} * 512)) --sizelimit $((${SIZE2} * 512)) "${img}")" 64 | if [ $? -ne 0 ]; then 65 | errexit "Unable to create ROOT loop device" 66 | fi 67 | } 68 | 69 | rmloop2() 70 | { 71 | if [ "${LOOP2}" != "" ]; then 72 | sync 73 | losetup -d "${LOOP2}" 74 | LOOP2="" 75 | fi 76 | } 77 | 78 | # Mount Image partitions 79 | mntimg() 80 | { 81 | MNTED=TRUE 82 | if [ ! -d "${mntpnt}/" ]; then 83 | mkdir "${mntpnt}/" 84 | if [ $? -ne 0 ]; then 85 | errexit "Unable to make ROOT partition mount point" 86 | fi 87 | fi 88 | mkloop2 89 | e2fsck -f -y "${LOOP2}" || errexit "Check image failed!" 90 | mount "${LOOP2}" "${mntpnt}/" 91 | if [ $? -ne 0 ]; then 92 | errexit "Unable to mount image ROOT partition" 93 | fi 94 | if [ ! -d "${mntpnt}/boot/" ]; then 95 | mkdir -p "${mntpnt}/boot/" 96 | if [ $? -ne 0 ]; then 97 | errexit "Unable to make BOOT partition mount point" 98 | fi 99 | fi 100 | mkloop1 101 | mount "${LOOP1}" "${mntpnt}/boot/" 102 | if [ $? -ne 0 ]; then 103 | errexit "Unable to mount image BOOT partition" 104 | fi 105 | } 106 | 107 | umntimg() 108 | { 109 | umount "${mntpnt}/boot/" 110 | if [ $? -ne 0 ]; then 111 | errexit "Unable to unmount image BOOT partition" 112 | fi 113 | rmloop1 114 | umount "${mntpnt}/" 115 | if [ $? -ne 0 ]; then 116 | errexit "Unable to unmount image ROOT partition" 117 | fi 118 | rmloop2 119 | rm -r "${mntpnt}/" 120 | MNTED=FALSE 121 | } 122 | 123 | errexit() 124 | { 125 | echo "" 126 | echo -e "\e[91m$1\e[39m" 127 | echo "" 128 | sync 129 | if [ "${MNTED}" = "TRUE" ]; then 130 | umount "${mntpnt}/boot/" 131 | umount "${mntpnt}/" && rm -rf "${mntpnt}/" 132 | fi 133 | rmloop1 134 | rmloop2 135 | exit 1 136 | } 137 | 138 | LOOP1="" 139 | LOOP2="" 140 | MNTED=FALSE 141 | 142 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 143 | 144 | if [ $(id -u) -ne 0 ]; then 145 | errexit "$0 must be run as root user" 146 | fi 147 | 148 | PGMNAME="$(basename $0)" 149 | for PID in $(pidof -x -o %PPID "${PGMNAME}"); do 150 | if [ ${PID} -ne $$ ]; then 151 | errexit "${PGMNAME} is already running" 152 | fi 153 | done 154 | 155 | rsync --version &> /dev/null 156 | if [ $? -ne 0 ]; then 157 | errexit "rsync not installed (run: apt-get install rsync)" 158 | fi 159 | 160 | if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then 161 | SYSTEMD=1 162 | elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then 163 | SYSTEMD=0 164 | else 165 | errexit "Unrecognized init system" 166 | fi 167 | 168 | if [ ${SYSTEMD} -eq 1 ]; then 169 | ROOT_PART="$(mount | sed -n 's|^/dev/\(.*\) on / .*|\1|p')" 170 | else 171 | if [ ! -h /dev/root ]; then 172 | errexit "/dev/root does not exist or is not a symlink" 173 | fi 174 | ROOT_PART="$(readlink /dev/root)" 175 | fi 176 | 177 | ROOT_TYPE=$(blkid "/dev/${ROOT_PART}" | sed -n 's|^.*TYPE="\(\S\+\)".*|\1|p') 178 | 179 | ROOT_DEV="${ROOT_PART:0:(${#ROOT_PART} - 1)}" 180 | if [ "${ROOT_DEV}" = "mmcblk0p" ]; then 181 | ROOT_DEV="${ROOT_DEV:0:(${#ROOT_DEV} - 1)}" 182 | fi 183 | 184 | PTUUID="$(blkid "/dev/${ROOT_DEV}" | sed -n 's|^.*PTUUID="\(\S\+\)".*|\1|p')" 185 | 186 | DEVSIZE=$(blockdev --getsize64 "/dev/${ROOT_PART}") 187 | BLKSIZE=$(blockdev --getbsz "/dev/${ROOT_PART}") 188 | BLKCNT=$((${DEVSIZE} / ${BLKSIZE})) 189 | INFO="$(df | grep /dev/root)" 190 | DFKSIZE=$(awk '{print $2}' <<< "${INFO}") 191 | DFKFREE=$(awk '{print $4}' <<< "${INFO}") 192 | ROOTSIZE=$((${BLKCNT} * ${BLKSIZE})) 193 | ROOTUSED=$(((${DFKSIZE} - ${DFKFREE}) * 1024)) 194 | IRFSMIN=$(((${ROOTUSED} + (${ADDBLK} * ${BLKSIZE}) + (${ONEMB} - 1)) / ${ONEMB})) 195 | IRFSMAX=$(((${ROOTSIZE} + (${ONEMB} - 1)) / ${ONEMB})) 196 | 197 | # Workaround unable to make ext4 fs in img if blksize is 512 198 | if [ "${BLKSIZE}" = "512" ]; then 199 | BLKSIZE="4096" 200 | fi 201 | 202 | img="$1" 203 | 204 | if [ -z "$img" ];then 205 | errexit "You must specify an img file location. For example: $0 /home/pi/backup.img" 206 | fi 207 | 208 | if [ ! "${img}" = "" ] || [ ! -f "${img}" ]; then 209 | # Create Image file 210 | IRFSSIZE="1048576" # number in MB - so 1 TB 211 | if [ -f "${img}" ]; then 212 | rm "${img}" 213 | if [ $? -ne 0 ]; then 214 | errexit "Unable to delete existing image file" 215 | fi 216 | fi 217 | ROOTEND=$((${ROOTBEG} + ((${IRFSSIZE} * ${ONEMB}) / 512) - 1)) 218 | truncate -s $(((${ROOTEND} + 1) * 512)) "${img}" 219 | if [ $? -ne 0 ]; then 220 | errexit "Unable to create image file" 221 | fi 222 | # create image/partitions 223 | sync 224 | fdisk "${img}" < /dev/null 225 | p 226 | n 227 | p 228 | 1 229 | ${BOOTBEG} 230 | +${BOOTSIZEM} 231 | t 232 | c 233 | p 234 | n 235 | p 236 | 2 237 | ${ROOTBEG} 238 | ${ROOTEND} 239 | p 240 | w 241 | EOF 242 | 243 | mkloop1 244 | mkloop2 245 | mkfs.vfat "${LOOP1}" > /dev/null 246 | if [ $? -ne 0 ]; then 247 | errexit "Unable to create image BOOT filesystem" 248 | fi 249 | dosfsck "${LOOP1}" > /dev/null 250 | if [ $? -ne 0 ]; then 251 | errexit "Image BOOT filesystem appears corrupted" 252 | fi 253 | if [ "${ROOT_TYPE}" = "f2fs" ]; then 254 | mkfs.f2fs "${LOOP2}" > /dev/null 255 | else 256 | mkfs.ext4 -q -b ${BLKSIZE} "${LOOP2}" > /dev/null 257 | fi 258 | if [ $? -ne 0 ]; then 259 | errexit "Unable to create image ROOT filesystem" 260 | fi 261 | rmloop2 262 | rmloop1 263 | # Initialise image PARTUUID 264 | fdisk "${img}" < /dev/null 265 | p 266 | x 267 | i 268 | 0x${PTUUID} 269 | r 270 | p 271 | w 272 | EOF 273 | # Create empty directories in image root partition 274 | mntimg 275 | mkdir "${mntpnt}/dev/" "${mntpnt}/media/" "${mntpnt}/mnt/" "${mntpnt}/proc/" "${mntpnt}/run/" "${mntpnt}/sys/" "${mntpnt}/tmp/" 276 | if [ $? -ne 0 ]; then 277 | errexit "Unable to create image directories" 278 | fi 279 | chmod a+rwxt "${mntpnt}/tmp/" 280 | umntimg 281 | echo "" 282 | echo "Starting full backup (for incremental backups, run: $0 ${img})" 283 | # END of create image/partitions 284 | fi 285 | 286 | # Check existing Image 287 | if [ -d "${img}" ]; then 288 | errexit "${img} is a directory" 289 | elif [ ! -f "${img}" ]; then 290 | errexit "${img} not found" 291 | fi 292 | echo "Starting incremental backup to ${img}" 293 | 294 | # rsync root partition 295 | mntimg 296 | sync 297 | 298 | rsync -aDHv --partial --numeric-ids --delete --force --exclude="${mntpnt}" --exclude='/dev' --exclude='/media' --exclude='/mnt' --exclude='/proc' --exclude='/run' --exclude='/sys' \ 299 | --exclude='/tmp' --exclude='lost\+found' --exclude="$img" --exclude='/etc/udev/rules.d/70-persistent-net.rules' --exclude='/var/lib/asterisk/astdb.sqlite3-journal' $rsyncargs / "${mntpnt}/" 300 | if [[ $? -ne 0 && $? -ne 24 ]]; then 301 | errexit "Rsync failed." 302 | fi 303 | sync 304 | umntimg 305 | -------------------------------------------------------------------------------- /functions/restore-pt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #this downloads a Raspbian partiton table and overwrites the partition table to the specified image 3 | #$1 is img file to overwrite partition table 4 | 5 | ############## 6 | #intended for img-maker internal use - this doesn't ensure input image exists. 7 | ############## 8 | 9 | DIRECTORY="$(dirname "$(dirname $0)")" 10 | 11 | sudo dd if="${DIRECTORY}/data/part-table.img" of="$1" bs=1M count=1 conv=notrunc 12 | 13 | #enlarge the image to raspbian full specs 14 | truncate "$1" -s '>10000000000' 15 | 16 | sudo "${DIRECTORY}/functions/image-shrink" "$1" 17 | -------------------------------------------------------------------------------- /functions/terminal-run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #$1 is the command to be run. 4 | #$2 is the title. 5 | 6 | #set to 1 to display which terminal is being used 7 | DEBUG=0 8 | 9 | if [ -f /usr/bin/lxterminal ];then 10 | lxterminal --title="$2" -e bash -c "$1" 11 | [ $DEBUG == 1 ] && echo lxterminal 12 | 13 | elif [ -f /usr/bin/xfce4-terminal ];then 14 | xfce4-terminal --title="$2" -x bash -c "$1" 15 | [ $DEBUG == 1 ] && echo xfce4-terminal 16 | 17 | elif [ -f /usr/bin/mate-terminal ];then 18 | #mate-terminal --title="$2" -e "bash -c "\""$1"\""" 19 | mate-terminal --title="$2" -x bash -c "$1" 20 | [ $DEBUG == 1 ] && echo mate-terminal 21 | 22 | elif [ -f /usr/bin/xterm ];then 23 | xterm -T "$2" -e bash -c "$1" 24 | [ $DEBUG == 1 ] && echo xterm 25 | 26 | elif [ -f /usr/bin/konsole ];then 27 | konsole -p tabtitle="$2" -e bash <(echo "$1") 28 | [ $DEBUG == 1 ] && echo konsole 29 | 30 | elif [ -f /usr/bin/terminator ];then 31 | terminator -T "$2" -x bash -c "$1" 32 | [ $DEBUG == 1 ] && echo terminator 33 | 34 | elif [ -f /usr/bin/gnome-terminal ];then 35 | gnome-terminal --title "$2" -x bash -c "$1" 36 | [ $DEBUG == 1 ] && echo gnome-terminal 37 | 38 | elif [ -f /usr/bin/qterminal ];then 39 | qterminal -e bash <(echo "$1") 40 | [ $DEBUG == 1 ] && echo qterminal 41 | 42 | elif [ -f /usr/bin/x-terminal-emulator ];then 43 | $(readlink -f /usr/bin/x-terminal-emulator) -e bash -c "$1" 44 | [ $DEBUG == 1 ] && echo x-terminal-emulator 45 | 46 | else 47 | echo "Failed to locate any terminal emulators!!!" 48 | exit 1 49 | fi -------------------------------------------------------------------------------- /functions/zerofree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/functions/zerofree -------------------------------------------------------------------------------- /functions/zerofree_runner: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIRECTORY="$(dirname "$(dirname $0)")" 4 | 5 | LOOP="$(sudo losetup -fP --show "$1")" 6 | trap "sudo losetup -d $LOOP" EXIT 7 | 8 | sudo "${DIRECTORY}/functions/zerofree" -v "${LOOP}p2" || exit 1 9 | 10 | sudo "${DIRECTORY}/functions/image-shrink" "$1" -------------------------------------------------------------------------------- /home: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIRECTORY="$(dirname $0)" 3 | 4 | #check for ppt updates 5 | 6 | LATEST_VER="$(wget -qO- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/master/data/version)" 7 | LOCAL_VER="$(head -n 1 "${DIRECTORY}/data/version")" 8 | 9 | echo "latest version: $LATEST_VER" 10 | echo "local version: $LOCAL_VER" 11 | 12 | #if run as menubutton, open default startup window 13 | if [ "$1" = 'menubutton' ];then 14 | defopen="$(cat "${DIRECTORY}/data/home.conf" | sed -n 2p)" 15 | echo "defopen: $defopen" 16 | if [ "$defopen" = "This window" ];then 17 | true #dont prompt for update since user will see it in home page 18 | elif [ "$defopen" = "USB Mode" ];then 19 | #display update dialog if update is available 20 | if [ ! -z $LATEST_VER ] && [ $LATEST_VER -gt $LOCAL_VER ];then 21 | yad --title='Pi Power Tools' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 22 | --text="Version ${LATEST_VER} is available. Install now?" \ 23 | --center --text-align=center \ 24 | --button="Not now!${DIRECTORY}/icons/forward.png:1" \ 25 | --button="Install!${DIRECTORY}/icons/download.png:0" 2>/dev/null && wget -O - https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/master/update | bash 26 | fi 27 | #and now go to usb mode 28 | "${DIRECTORY}/usb-mode" & 29 | exit 0 30 | elif [ "$defopen" = "IMG Mode" ];then 31 | #display update dialog if update is available 32 | if [ ! -z $LATEST_VER ] && [ $LATEST_VER -gt $LOCAL_VER ];then 33 | yad --title='Pi Power Tools' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 34 | --text="Version ${LATEST_VER} is available. Install now?" \ 35 | --center --text-align=center \ 36 | --button="Not now!${DIRECTORY}/icons/forward.png:1" \ 37 | --button="Install!${DIRECTORY}/icons/download.png:0" 2>/dev/null && wget -O - https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/master/update | bash 38 | fi 39 | #and now go to img mode 40 | "${DIRECTORY}/img-mode" & 41 | exit 0 42 | elif [ "$defopen" = "Flash" ];then 43 | "${DIRECTORY}/flash" 44 | else 45 | echo "conf corrupted or nonexistent" 46 | rm "${DIRECTORY}/data/home.conf" 47 | #make conf file 48 | echo " 49 | This window 50 | FALSE 51 | 52 | Desktop 53 | 54 | TRUE" > "${DIRECTORY}/data/home.conf" 55 | fi 56 | fi 57 | 58 | "${DIRECTORY}/installgui" yad 59 | 60 | while true;do 61 | if [ ! -z $LATEST_VER ] && [ $LATEST_VER -gt $LOCAL_VER ];then 62 | addtext=" 63 | Version $LATEST_VER is available. Click Update to install it. 64 | " 65 | addbutton="--button=Update!${DIRECTORY}/icons/download.png!Installs the latest version of Pi Power Tools.:6" 66 | elif [ "$(cat "${DIRECTORY}/data/home.conf" | sed -n 3p)" = 'TRUE' ];then 67 | #if update enabled always 68 | addbutton="--button=Update!${DIRECTORY}/icons/download.png!Click this to factory reset Pi Power Tools.:6" 69 | else 70 | addbutton='' 71 | fi 72 | output="$(echo "Welcome to Pi Power Tools! 73 | General Purpose Raspbian Image & SD Card Manager 74 | $addtext 75 | Click USB Mode or IMG Mode to get started. 76 | Curious what a button does? Just hover your mouse over it! 77 | 78 | For full help click here: 79 | https://github.com/Botspot/Pi-Power-Tools#instructions" | yad --text-info \ 80 | --wrap --justify=center --fore=#80FFFF --back=#000000 --title="Pi Power Tools - Home" \ 81 | --width=530 --height=260 --center --wrap --window-icon="${DIRECTORY}/icons/logo-64.png" \ 82 | --image=${DIRECTORY}/icons/logo-tall.png --show-uri --fontname="12" \ 83 | "$addbutton" \ 84 | --button='Settings'!"${DIRECTORY}/icons/settings.png"!'Customize your experience with Pi Power Tools.':4 \ 85 | --button='USB Mode'!"${DIRECTORY}/icons/usb-16.png"!'Manage a USB device that has Raspbian flashed to it.':2 \ 86 | --button='IMG Mode'!"${DIRECTORY}/icons/img-16.png"!'Everything you need to customize a Raspbian image.':0 \ 87 | 2>/dev/null)" 88 | button=$? 89 | 90 | case $button in 91 | 92 | 1) 93 | echo "Error" 94 | exit 1 95 | ;; 96 | 97 | 2) 98 | echo "USB mode" 99 | "${DIRECTORY}/usb-mode" & 100 | exit 0 101 | ;; 102 | 103 | 0) 104 | echo "IMG mode" 105 | "${DIRECTORY}/img-mode" & 106 | exit 0 107 | ;; 108 | 109 | 4) 110 | echo "Settings" 111 | settings="$(cat "${DIRECTORY}/data/home.conf")" 112 | echo "$settings" 113 | openoptions="This window 114 | USB Mode 115 | IMG Mode 116 | Flash" 117 | if [ -z "$(echo "$openoptions" | grep "$settings")" ];then 118 | echo "conf corrupted or nonexistent" 119 | rm "${DIRECTORY}/data/home.conf" 120 | echo -e " 121 | This window 122 | FALSE 123 | 124 | Desktop 125 | 1 126 | " > "${DIRECTORY}/data/home.conf" 127 | exit 0 128 | fi 129 | sorted_open="$(echo "$settings" | sed -n 2p)!$(echo "$openoptions" | grep -v "$(echo "$settings" | sed -n 2p)" | tr '\n' '!' )" 130 | 131 | bootoptions="Desktop 132 | Console - autologin 133 | Console - no autologin" 134 | 135 | sorted_boot="$(echo "$settings" | sed -n 5p)!$(echo "$bootoptions" | grep -v "$(echo "$settings" | sed -n 5p)" | tr '\n' '!' )" 136 | output="$(yad --form --window-icon="${DIRECTORY}/icons/logo-64.png" \ 137 | --title="Settings" --center \ 138 | --field="Home Settings:LBL" '' \ 139 | --field="Menu button opens::CB" "${sorted_open::-1}" \ 140 | --field="Show Update button even if latest version:CHK" "$(echo "$settings" | sed -n 3p)" \ 141 | --field="Boot Settings:LBL" '' \ 142 | --field="Boot to::CB" "${sorted_boot::-1}" \ 143 | --field="IMG Mode Settings:LBL" '' \ 144 | --field="Run zerofree during Shrink:CHK" "$(echo "$settings" | sed -n 7p)" \ 145 | --field="GB of free space:NUM" "$(echo "$settings" | sed -n 8p)!1..1024" \ 146 | --button='Cancel'!"${DIRECTORY}/icons/back.png":1 \ 147 | --button='Save'!"${DIRECTORY}/icons/save.png":0 \ 148 | --separator='\n' 2>/dev/null)" 149 | button=$? 150 | if [ $button -eq 0 ];then 151 | echo "output: ${output}EOO" 152 | #save to conf file 153 | echo "$output" > "${DIRECTORY}/data/home.conf" 154 | 155 | #generate vdesktop.conf 156 | if [ "$(echo "$output" | sed -n 5p)" = "Desktop" ];then 157 | echo "gui" > "${DIRECTORY}/data/vdesktop.conf" 158 | elif [ "$(echo "$output" | sed -n 5p)" = 'Console - autologin' ];then 159 | echo "cli-login" > "${DIRECTORY}/data/vdesktop.conf" 160 | elif [ "$(echo "$output" | sed -n 5p)" = 'Console - no autologin' ];then 161 | echo "cli" > "${DIRECTORY}/data/vdesktop.conf" 162 | else 163 | echo "Failed to generate vdesktop config." 164 | fi 165 | 166 | fi 167 | ;; 168 | 169 | 6) 170 | echo "Update" 171 | wget -O - https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/master/update | bash & 172 | exit 0 173 | ;; 174 | *) 175 | #any other button 176 | echo "exiting" 177 | echo '' 178 | exit 0 179 | ;; 180 | esac 181 | done 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /icons/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/back.png -------------------------------------------------------------------------------- /icons/backup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/backup.png -------------------------------------------------------------------------------- /icons/boot_mode.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/boot_mode.gif -------------------------------------------------------------------------------- /icons/burn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/burn.png -------------------------------------------------------------------------------- /icons/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/check.png -------------------------------------------------------------------------------- /icons/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/download.png -------------------------------------------------------------------------------- /icons/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/exit.png -------------------------------------------------------------------------------- /icons/expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/expand.png -------------------------------------------------------------------------------- /icons/forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/forward.png -------------------------------------------------------------------------------- /icons/gparted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/gparted.png -------------------------------------------------------------------------------- /icons/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/home.png -------------------------------------------------------------------------------- /icons/img-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/img-16.png -------------------------------------------------------------------------------- /icons/img-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/img-48.png -------------------------------------------------------------------------------- /icons/img-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/img-64.png -------------------------------------------------------------------------------- /icons/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/install.png -------------------------------------------------------------------------------- /icons/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/link.png -------------------------------------------------------------------------------- /icons/logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/logo-16.png -------------------------------------------------------------------------------- /icons/logo-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/logo-64.png -------------------------------------------------------------------------------- /icons/logo-tall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/logo-tall.png -------------------------------------------------------------------------------- /icons/mounted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/mounted.png -------------------------------------------------------------------------------- /icons/power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/power.png -------------------------------------------------------------------------------- /icons/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/refresh.png -------------------------------------------------------------------------------- /icons/rpi-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/rpi-logo.png -------------------------------------------------------------------------------- /icons/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/save.png -------------------------------------------------------------------------------- /icons/sd-to-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/sd-to-img.png -------------------------------------------------------------------------------- /icons/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/search.png -------------------------------------------------------------------------------- /icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/settings.png -------------------------------------------------------------------------------- /icons/shrink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/shrink.png -------------------------------------------------------------------------------- /icons/tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/tool.png -------------------------------------------------------------------------------- /icons/toolbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/toolbox.png -------------------------------------------------------------------------------- /icons/trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/trash.png -------------------------------------------------------------------------------- /icons/twister.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/twister.png -------------------------------------------------------------------------------- /icons/unmounted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/unmounted.png -------------------------------------------------------------------------------- /icons/usb-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/usb-16.png -------------------------------------------------------------------------------- /icons/usb-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/usb-64.png -------------------------------------------------------------------------------- /icons/usb-mount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/usb-mount.png -------------------------------------------------------------------------------- /icons/usb-unmount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/usb-unmount.png -------------------------------------------------------------------------------- /icons/vector/burn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/vector/burn.png -------------------------------------------------------------------------------- /icons/vector/diskimage-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /icons/vector/diskimage.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /icons/vector/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /icons/vector/fire-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /icons/vector/gparted.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /icons/vector/green-dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /icons/vector/install.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /icons/vector/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /icons/vector/power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/vector/power.png -------------------------------------------------------------------------------- /icons/vector/power.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /icons/vector/red-dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /icons/vector/save.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /icons/vector/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/vector/search.png -------------------------------------------------------------------------------- /icons/vector/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /icons/vector/tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/4eccd96ab47188b0dd6d425bcf61e0c8adafab77/icons/vector/tool.png -------------------------------------------------------------------------------- /icons/vector/tool.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /icons/vector/toolbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /icons/vector/usb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /icons/vector/vd-tall.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /icons/vector/vd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /img-mode: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIRECTORY="$(dirname "$0")" 3 | 4 | #ensure prerequesites are installed 5 | "${DIRECTORY}/installgui" "yad" &>/dev/null || exit 0 6 | 7 | #kill all subprocesses on exit 8 | trap 'kill $(jobs -p)' EXIT 9 | trap 'killall img-mode' EXIT 10 | 11 | #ERRORBOX 12 | errorbox(){ 13 | #$1 is message 14 | #$2 is type: fatal or left blank 15 | if [ -z "$2" ];then 16 | yad --title='Pi Power Tools - Error' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 17 | --text="Oh no!\n\n$1\nTry again?" \ 18 | --center --width=300 --text-align=center \ 19 | --button="OK!${DIRECTORY}/icons/forward.png:0" 2>/dev/null 20 | elif [ "$2" = 'fatal' ];then 21 | yad --title='Pi Power Tools - Error' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 22 | --text="Fatal error:\n$1\nClosing in 30 seconds." \ 23 | --center --width=300 --text-align=center \ 24 | --timeout=30 --timeout-indicator=bottom \ 25 | --button="Close!${DIRECTORY}/icons/exit.png:0" 2>/dev/null 26 | exit 1 27 | else 28 | errorbox 'errorbox error' fatal 29 | fi 30 | } 31 | 32 | 33 | function imgfromsystem { 34 | 35 | echo "This feature creates an .img file from your Pi"\'"s internal SD card. 36 | You can save the img anywhere, but please make sure there is enough free space to save it! 37 | Rsync copies individual files to the image instead of using dd. This approach is much faster, saves space, and lets you do incremental backups." | yad \ 38 | --title='Please read this' --window-icon="${DIRECTORY}/icons/logo-64.png" --text-info \ 39 | --center --wrap --width=500 --height=200 --fontname=12 \ 40 | --button=Cancel!"${DIRECTORY}/icons/exit.png":1 --button="Continue"!"${DIRECTORY}/icons/forward.png":0 41 | 42 | #only do anything if user clicks Continue 43 | if [ $? == 0 ];then 44 | savedimg="$( yad --file --save --center --text="Tip: You can select a pre-existing img file and rsync will only copy new/changed files." --filename="$HOME/mynewimage.img" --title='Where to save this image?' --window-icon="${DIRECTORY}/icons/logo-64.png")" 45 | 46 | if [ ! -z "$savedimg" ];then 47 | ( 48 | 49 | echo "Checking for large files on filesystem..." 50 | lfiles="$(find /home -mount -type f -size +1G 2>/dev/null)" 51 | echo "Done" 52 | 53 | if [ ! -z "$lfiles" ];then 54 | #indent each file 55 | PREIFS="$IFS" 56 | IFS=$'\n' 57 | for file in $lfiles 58 | do 59 | displaylfiles="${displaylfiles}\n ${file}" 60 | done 61 | IFS="$PREIFS" 62 | 63 | echo -e "These files are larger than 1GB:\n${displaylfiles}" | yad --text-info \ 64 | --title='Exclude large files?' --window-icon="${DIRECTORY}/icons/logo-64.png" --text-info \ 65 | --center --wrap --width=500 --height=200 --fontname=12 \ 66 | --button='Include these files in the img':1 --button="Skip these files":0 67 | if [ "$?" == '0' ];then 68 | #'large files not included' enabled 69 | rsyncargs="${rsyncargs} --max-size=1g" 70 | fi 71 | fi 72 | 73 | #if img exists, expand it to 1TB 74 | if [ -f "$savedimg" ];then 75 | echo "Expanding image to a 1 TB sparse file. It won"\'"t actually take up 1TB, don"\'"t worry." 76 | sudo "${DIRECTORY}/functions/image-shrink" "$savedimg" $((1024*1024)) || errorbox "Failed to expand image!" fatal 77 | fi 78 | 79 | echo "Starting backup now." 80 | sudo "${DIRECTORY}/functions/milliways-image-backup" "$savedimg" "$rsyncargs" || errorbox "Failed to backup image!" fatal 81 | 82 | echo "Backup finished. Shrinking image now." 83 | sudo "${DIRECTORY}/functions/image-shrink" "$savedimg" 0 || errorbox "Failed to shrink image!" fatal 84 | 85 | echo "All done. Closing in 10 seconds." 86 | sleep 10 87 | pkill yad 88 | ) 2>&1 | yad --text-info --tail \ 89 | --center --width=900 --height=500 --window-icon="${DIRECTORY}/icons/logo-64.png" --title='Creating image from this SD card' \ 90 | --button=Cancel!"${DIRECTORY}/icons/exit.png":"pkill -P $$" --back=black --fore='#0040c0' 91 | fi 92 | fi 93 | img='' 94 | } 95 | 96 | #DOWNLOAD 97 | function download { 98 | downloadicons="${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/rpi-logo.png\n${DIRECTORY}/icons/twister.png" 99 | 100 | downloadlist="$(cat ${DIRECTORY}/data/mirrors | grep -v ^\# | awk -F ' ' '{print $1" "$2" "$2" "$3}')" 101 | 102 | downloadlist="$(paste -d '\n' <(echo -e "$downloadicons") <(echo -e "$downloadlist"))" 103 | 104 | downloadlist="$(echo "$downloadlist" | tr ' ' '\n')" 105 | 106 | repeat=1 107 | while [ $repeat = 1 ];do 108 | output="$(echo -e "$downloadlist" | yad --list --separator='\n' --center --height=400 --width=700 \ 109 | --title='Download new IMG' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 110 | --text="Select IMG to download." --column=:IMG --column=Name --column='Size:SZ' --column='bytes:HD' --column=URL \ 111 | --button='Back'!"${DIRECTORY}/icons/back.png"!:1 \ 112 | --button='Download'!"${DIRECTORY}/icons/download.png":0 \ 113 | 2>/dev/null)" 114 | button=$? 115 | 116 | if [ ! $button -eq 0 ];then 117 | img='' 118 | return 119 | fi 120 | 121 | echo "Output: ${output}EOO" 122 | 123 | if [ -z "$output" ];then 124 | errorbox "You need to select a download site." 125 | else 126 | 127 | dlname="$(echo "$output" | sed -n 2p)" 128 | dlsize="$(echo "$output" | sed -n 4p)" 129 | dlurl="$(echo "$output" | sed -n 5p)" 130 | 131 | #getting the filesize over the internet doesn't work for Raspbian Full for some reason. 132 | #dlsize="$(wget -qO- "$dlurl" | dd bs=1M count=1 | busybox unzip -lq - | sed -n 3p | awk '{print $1}')" 133 | 134 | dlfilename="$(wget -qO- "$dlurl" | dd bs=1M count=1 2>/dev/null | busybox unzip -lq - 2>/dev/null | sed -n 3p | awk '{print $4}')" 135 | 136 | echo "Download URL: $dlurl" 137 | echo "Download size: $dlsize" 138 | echo "Download name: $dlname" 139 | echo "Download filename: $dlfilename" 140 | 141 | if [ -z "$dlsize" ] || [ -z "$dlfilename" ];then 142 | errorbox "Could not connect to server. Likely no Internet connection." fatal 143 | fi 144 | repeat=0 145 | fi 146 | 147 | done 148 | #past this point, a download url is selected. 149 | startseconds=$(cat /proc/uptime | awk '{print $1}' | awk '{print int($1+0.5)}') 150 | (echo "0" 151 | 152 | 153 | #Download-img is a temporary folder the disk image is downloaded to. 154 | #The script has to get the full filename. 155 | #Temporarily storing in this folder prevents issues if there is already a img in the home directory. 156 | echo "# Removing residual folder ${HOME}/download-img" 157 | rm -rf "${HOME}/download-img" 158 | 159 | mkdir "${HOME}/download-img" 160 | cd "${HOME}/download-img" 161 | curbytes=1 162 | while true;do 163 | curbytes="$(ls -al * 2>/dev/null | awk '{print $5}')" 164 | #echo $curbytes 165 | progress="$((curbytes/(dlsize/100)))" 166 | if [ $progress -gt 98 ];then 167 | progress=98 168 | fi 169 | echo $progress 170 | echo "# Downloading ${dlname}: $(numfmt --to=iec --suffix=B --padding=7 $curbytes)" 171 | sleep 0.1 172 | done & 173 | childpid=$! 174 | trap "kill $childpid" EXIT 175 | 176 | #add first 10MB to cache. This speeds the download process. 177 | wget -qO- "$dlurl" | "${DIRECTORY}/functions/buffer" | busybox unzip - -p | dd ibs=10M obs=10M | dd bs=10M count=1 of=/dev/null 2>/dev/null 178 | 179 | trap "killall wget" EXIT 180 | wget -qO- "$dlurl" | "${DIRECTORY}/functions/buffer" | busybox unzip - -p | "${DIRECTORY}/functions/buffer" | dd of="${HOME}/download-img/ppt-dl-${dlfilename}" bs=10M 181 | kill "$childpid" 182 | 183 | sleep 5 184 | imagelocation="$(find "$(pwd)" -type f)" 185 | echo "Image location: $imagelocation" 186 | filename="$(echo "$imagelocation" | awk -F '/' 'NF>1{print $NF}')" 187 | 188 | cd "$HOME" 189 | 190 | echo "# Moving to $HOME" 191 | mv "$imagelocation" "$HOME" 192 | 193 | echo "# deleting temporary folder" 194 | rm -r "${HOME}/download-img" 195 | 196 | echo "${HOME}/${filename}" >> "${DIRECTORY}/data/imglist" 197 | img="${HOME}/${filename}" 198 | 199 | echo "# Done." 200 | echo 100 201 | ) | 202 | yad --progress --title="Downloading $dlname" --auto-close --auto-kill --center \ 203 | --window-icon="${DIRECTORY}/icons/logo-64.png" \ 204 | --button='Cancel'!"${DIRECTORY}/icons/exit.png":1 \ 205 | 2>/dev/null || echo 'canceled' 206 | echo "closed" 207 | endseconds=$(cat /proc/uptime | awk '{print $1}' | awk '{print int($1+0.5)}') 208 | (echo "# Download finished. Elapsed time: $((endseconds-startseconds)) seconds." 209 | ) | yad --progress --title="Downloading $dlname" --center \ 210 | --window-icon="${DIRECTORY}/icons/logo-64.png" \ 211 | --button='OK'!"${DIRECTORY}/icons/forward.png":0 \ 212 | 2>/dev/null 213 | } 214 | 215 | #SELECT IMG 216 | function selectimg { 217 | while [ -z "$img" ] || [ ! -f "$img" ];do 218 | 219 | #parse imglist 220 | imglist="$(${DIRECTORY}/functions/imglist-parser || errorbox 'Failed to parse imglist!')" 221 | 222 | img='' 223 | img="$(yad --form --separator='\n' \ 224 | --title='Pi Power Tools - IMG Mode' --center --window-icon="${DIRECTORY}/icons/logo-64.png" \ 225 | --field="Select an img to continue.":LBL '' \ 226 | --field=":CBE" "$imglist" \ 227 | --button='Home'!"${DIRECTORY}/icons/home.png"!'Back to home':4 \ 228 | --button='SD to IMG'!"${DIRECTORY}/icons/sd-to-img.png"!"Creates an .img file from your Pi"\'"s internal SD card.:6" \ 229 | --button='Download New'!"${DIRECTORY}/icons/download.png":2 \ 230 | --button=Next!"${DIRECTORY}/icons/forward.png":0 \ 231 | 2>/dev/null)" 232 | button=$? 233 | 234 | #if neither next nor download nor usb mode buttons pressed 235 | if [ ! $button -eq 2 ]&&[ ! $button -eq 0 ]&&[ ! $button -eq 4 ]&&[ ! $button -eq 6 ];then 236 | #exit the program 237 | exit 0 238 | fi 239 | 240 | if [ $button -eq 4 ];then 241 | img='' 242 | "${DIRECTORY}/home" & 243 | exit 0 244 | fi 245 | 246 | #if clicked next 247 | if [ $button -eq 0 ];then 248 | if [ -z $img ];then 249 | errorbox 'No image specified.' 250 | else 251 | #check if file:// appended to filename 252 | if [[ "$img" == *"file://"* ]];then 253 | img="${img:8}" 254 | echo -e "removing file:// from filename" 255 | #take off last letter 256 | img="${img::-1}" 257 | else 258 | #remove blank line break 259 | img="$(echo -e "$img" | sed -n 2p)" 260 | fi 261 | fi 262 | fi 263 | 264 | #if clicked download 265 | if [ $button -eq 2 ];then 266 | img='' 267 | download 268 | fi 269 | 270 | #if clicked IMG from this SD 271 | if [ $button -eq 6 ];then 272 | img='' 273 | imgfromsystem 274 | fi 275 | 276 | done 277 | echo "$img" >> "${DIRECTORY}/data/imglist" 278 | } 279 | 280 | if [ ! -z $1 ];then 281 | img="$1" 282 | if [ -f "$img" ] && [[ "$img" == *".img" ]];then 283 | #file exists 284 | #clear variables 285 | set '' 286 | else 287 | echo "variable: $1" 288 | errorbox "The specified IMG file does not exist." fatal 289 | fi 290 | fi 291 | 292 | while true;do 293 | 294 | if [ -z "$img" ];then 295 | selectimg 296 | fi 297 | 298 | #if yad exits uncleanly it leaves behind a problematic memory file 299 | #this deletes it 300 | ipcrm -M 12345 &>/dev/null 301 | 302 | echo "Image file selected: $img" 303 | output="$(yad --form --columns=4 --separator='\n' \ 304 | --title='Pi Power Tools - IMG Mode' --center --window-icon="${DIRECTORY}/icons/logo-64.png" \ 305 | --text="Editing $img 306 | IMG-specific tools:" \ 307 | --field='Reset PT'!"${DIRECTORY}/icons/backup.png"!'Messed up the partitions? This tries to fix it. 308 | Reverts the partition table to the default one from Raspbian Buster.':FBTN "${DIRECTORY}/functions/terminal-run \" 309 | if ${DIRECTORY}/functions/restore-pt $img ; then 310 | echo -en '\n\e[32mCommand succeeded.\e[39m\nClosing in 10 seconds.' 311 | sleep 10 312 | else 313 | echo -en '\n\e[31mCommand failed!\e[39m\nPress Enter to exit.' 314 | read enter 315 | fi 316 | \" \"Resetting partition table of $img\" " \ 317 | --field="Advmount"!"${DIRECTORY}/icons/usb-mount.png"!'Control exactly how and where to mount each partition':FBTN "${DIRECTORY}/functions/advmount $img" \ 318 | --field="Shrink"!"${DIRECTORY}/icons/shrink.png"!'Removes all free space. IMG is made as small as possible.':FBTN "${DIRECTORY}/functions/terminal-run \" 319 | if sudo ${DIRECTORY}/functions/image-shrink $img 0; then 320 | 321 | #run zerofree if enabled 322 | if [ $(cat "${DIRECTORY}/data/home.conf" | sed -n 7p) = TRUE ];then 323 | echo -e '\e[32mCommand succeeded.\e[39m Now running zerofree:' 324 | ${DIRECTORY}/functions/zerofree_runner $img 325 | else 326 | echo -en '\n\e[32mCommand succeeded.\e[39m\nClosing in 10 seconds.' 327 | fi 328 | sleep 10 329 | else 330 | echo -en '\n\e[31mCommand failed!\e[39m\nPress Enter to exit.' 331 | read enter 332 | fi 333 | \" \"Shrinking $img\"" \ 334 | --field="$(cat "${DIRECTORY}/data/home.conf" | sed -n 8p)GB Free"!"${DIRECTORY}/icons/expand.png"!'Ran out of space? Add some free space!':FBTN "${DIRECTORY}/functions/terminal-run \" 335 | if sudo ${DIRECTORY}/functions/image-shrink $img $(($(cat "${DIRECTORY}/data/home.conf" | sed -n 8p)*1024)) ; then 336 | echo -en '\n\e[32mCommand succeeded.\e[39m\nClosing in 10 seconds.' 337 | sleep 10 338 | else 339 | echo -en '\n\e[31mCommand failed!\e[39m\nPress Enter to exit.' 340 | read enter 341 | fi 342 | \" \"Adding $(cat "${DIRECTORY}/data/home.conf" | sed -n 8p)GB free space to $img\"" \ 343 | \ 344 | --button=Back!"${DIRECTORY}/icons/back.png"!:3 \ 345 | --button=Flash!"${DIRECTORY}/icons/burn.png"!'Copy everything from this img to a device.:2' \ 346 | --button=Boot!"${DIRECTORY}/icons/power.png"!'Boots the selected like a virtual machine.:4' \ 347 | --button=View!"${DIRECTORY}/icons/search.png"!'Browse the contents like you would a real storage device.:6' \ 348 | --button=Edit!"${DIRECTORY}/icons/gparted.png"!'This edits the partitions using gparted.:8' \ 349 | --buttons-layout=end \ 350 | 2>/dev/null)" 351 | 352 | button=$? #get exit code to determine which button was pressed 353 | echo "${output}EOO" 354 | #echo "$button" 355 | 356 | if [ $button -eq 252 ];then #if window manager x was pressed 357 | exit 0 358 | fi 359 | 360 | mntpnt="$(cat ${DIRECTORY}/data/mountpoint)" 361 | case $button in 362 | 363 | 1) 364 | errorbox "YAD exited with an error." fatal 365 | ;; 366 | 3) 367 | echo "Back" 368 | img='' 369 | ;; 370 | 371 | 4) 372 | echo "Boot" 373 | "${DIRECTORY}/installgui" "systemd-container xserver-xephyr expect" &>/dev/null || exit 0 374 | "${DIRECTORY}/functions/terminal-run" "sudo ${DIRECTORY}/vdesktop/vdesktop $img $(cat "${DIRECTORY}/data/vdesktop.conf");sleep 10" "Boot Console" 375 | sleep 10 376 | #wait until vdesktop is closed before reopening UI 377 | while ps -C vdesktop >/dev/null;do sleep 1; done 378 | ;; 379 | 380 | 6) 381 | echo "View" 382 | LOOP="$(sudo losetup -fP --show $img)" #create loopback device 383 | 384 | sudo mkdir "$mntpnt" 2>/dev/null 385 | 386 | sudo mount -o rw "${LOOP}p2" "$mntpnt" 387 | sudo mount -o rw "${LOOP}p1" "${mntpnt}/boot" 388 | 389 | pcmanfm -n "$mntpnt" #view the files 390 | yad --title='Viewing IMG' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 391 | --text="Close this window when you are finished viewing the files." \ 392 | --center --text-align=center \ 393 | --button="Close!${DIRECTORY}/icons/exit.png:1" 2>/dev/null 394 | sudo losetup -d "$LOOP" &>/dev/null #delete loopback device 395 | sudo umount -fl "${mntpnt}/boot" &>/dev/null 396 | sudo umount -fl "$mntpnt" &>/dev/null 397 | ;; 398 | 399 | 8) 400 | "${DIRECTORY}/installgui" "gparted" &>/dev/null || exit 0 401 | echo image 402 | LOOP="$(sudo losetup -fP --show $img)" #create loopback device 403 | 404 | echo $LOOP 405 | if [ -z $LOOP ];then 406 | echo "Could not create loopback device." 407 | exit 1 408 | fi 409 | sudo gparted "$LOOP" 410 | sudo losetup -d "$LOOP" &>/dev/null #delete loopback device 411 | ;; 412 | 2) 413 | echo "Flash" 414 | "${DIRECTORY}/flash" "$img" 415 | ;; 416 | *) 417 | echo "unknown" 418 | errorbox "Unknown button pressed: $button" fatal 419 | ;; 420 | esac 421 | echo "" 422 | done 423 | exit 424 | -------------------------------------------------------------------------------- /installgui: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIRECTORY="$(dirname $0)" 3 | #variable 1 is a space-seperated list of packages 4 | 5 | NUM_PKGS=$(echo "$1" | wc -w) 6 | #echo "$NUM_PKGS" 7 | #repeat for each package listed 8 | i=1 9 | while [ $i -le "$NUM_PKGS" ]; do 10 | REQ_PKG="$(echo $1 | cut -d " " -f $i)" 11 | #echo "$REQ_PKG" 12 | 13 | PKG_OK=$(dpkg-query -W --showformat='${Status}\n' "$REQ_PKG"|grep "install ok installed") 14 | if [ "" == "$PKG_OK" ]; then 15 | PKG_LIST="${PKG_LIST} ${REQ_PKG}" #add package to install list 16 | fi 17 | #echo $i 18 | PKG_LIST="$(echo "$PKG_LIST" | sed -e 's/^ //g')" 19 | i=$((i+1)) 20 | done 21 | 22 | #list created that only has packages not installed 23 | #remove spaces 24 | 25 | echo "To be installed: ${PKG_LIST}" 26 | 27 | #if blank or contains only spaces 28 | if [ ! -z "$PKG_LIST" ];then 29 | 30 | #has newline chars to display properly 31 | package_list="$(echo "$PKG_LIST" | tr ' ' '\n')" 32 | 33 | #If yad exists, use a yad dialog 34 | if [ -f /usr/bin/yad ];then 35 | echo -e "$package_list" | yad --list --no-click --no-columns --separator='\n' \ 36 | --title='Pi Power Tools' --center --window-icon="${DIRECTORY}/icons/logo-64.png" \ 37 | --text=" These packages are required but not installed. \n Install them now?" \ 38 | --column=Package \ 39 | --button="No!${DIRECTORY}/icons/exit.png:1" \ 40 | --button="Yes!${DIRECTORY}/icons/check.png:0" \ 41 | --image="${DIRECTORY}/icons/install.png" \ 42 | --print-column=2 --no-selection 2>/dev/null 43 | button=$? 44 | else #if yad does not exist then fallback to zenity 45 | echo -e "$package_list" | zenity --title='Pi Power Tools' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 46 | --list --text=" These packages are required but not installed. \n Install them now?" \ 47 | --ok-label=Yes --cancel-label=No \ 48 | --column=foo --hide-header 2>/dev/null 49 | button=$? 50 | fi 51 | 52 | #echo $button 53 | if [ ! $button -eq 0 ];then 54 | echo "User declined." 55 | exit 1 56 | fi 57 | 58 | echo $PKG_LIST > "${DIRECTORY}/installedpackages" 59 | "${DIRECTORY}/functions/terminal-run" "sudo apt install -y $PKG_LIST;echo -e '\nInstallation finished.\nClosing in 5 seconds.';sleep 5" "Installing packages..." 60 | if [ ! $? -eq 0 ];then #if apt exits with error 61 | echo -e "Could not install the packages!\nTry installing them yourself by running this in the terminal:\nsudo apt install $PGK_LIST" | yad --text-info \ 62 | --width=700 --height=150 --title='Pi Power Tools' --center \ 63 | --button="OK!${DIRECTORY}/icons/check.png:0" --window-icon="${DIRECTORY}/icons/logo-64.png" 2>/dev/null 64 | exit 1 65 | fi 66 | sleep 1 67 | #wait until apt process finishes 68 | while ps -C apt,dpkg >/dev/null;do 69 | sleep 1 70 | done 71 | 72 | fi 73 | #all of the above is skipped if there are no new packages to install. 74 | 75 | -------------------------------------------------------------------------------- /update: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -f ${HOME}/logo-64.png 4 | #get icon ahead of time so zenity can use it 5 | wget https://raw.githubusercontent.com/Botspot/Pi-Power-Tools/master/icons/logo-64.png 6 | 7 | trap "rm ${HOME}/logo-64.png" EXIT 8 | 9 | ( 10 | echo 0 11 | 12 | echo "# Downloading PPT to ${HOME}/Pi-Power-Tools..." 13 | cd $HOME 14 | rm -rf ${HOME}/Pi-Power-Tools.old 15 | mv -f ${HOME}/Pi-Power-Tools ${HOME}/Pi-Power-Tools.old 2>/dev/null 16 | git clone https://github.com/Botspot/Pi-Power-Tools || exit 1 17 | 18 | #transfer old config 19 | if [ -d "${HOME}/Pi-Power-Tools.old/" ]; then 20 | cp ${HOME}/Pi-Power-Tools.old/data/imglist ${HOME}/Pi-Power-Tools/data/ 21 | cp ${HOME}/Pi-Power-Tools.old/data/ziplist ${HOME}/Pi-Power-Tools/data/ 22 | fi 23 | sleep 3 24 | 25 | echo 25 26 | echo "# Downloading Vdesktop..." 27 | cd ${HOME}/Pi-Power-Tools 28 | git clone https://github.com/Botspot/vdesktop || exit 1 29 | sleep 1 30 | 31 | echo 50 32 | echo "# Click Yes to install required packages." 33 | ${HOME}/Pi-Power-Tools/installgui "yad" || exit 0 34 | 35 | echo 75 36 | echo "# Creating menu button..." 37 | echo -n '' > ${HOME}/.local/share/applications/ppt.desktop 38 | echo "[Desktop Entry] 39 | Name=Pi Power Tools 40 | Comment=General Purpose Raspbian Image & SD Card Manager 41 | Exec=${HOME}/Pi-Power-Tools/home menubutton 42 | Icon=${HOME}/Pi-Power-Tools/icons/logo-64.png 43 | Terminal=false 44 | Type=Application 45 | Categories=Utility;" >> ${HOME}/.local/share/applications/ppt.desktop 46 | 47 | #also copy to desktop 48 | cp ${HOME}/.local/share/applications/ppt.desktop ${HOME}/Desktop 49 | sleep 5 50 | 51 | echo 100 52 | echo -e "# Finished! Pi Power Tools is located in Menu --> Accessories." 53 | #sleep 5 54 | ) | 55 | zenity --progress --title='PPT Installer' --window-icon="${HOME}/logo-64.png" \ 56 | --width=300 --ok-label=Close 2>/dev/null 57 | #${HOME}/Pi-Power-Tools/pi-power-tools & 58 | -------------------------------------------------------------------------------- /usb-mode: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIRECTORY="$(dirname $0)" 3 | 4 | #ensure prerequesites are installed 5 | ${DIRECTORY}/installgui "yad" &>/dev/null || exit 0 6 | 7 | #ERRORBOX 8 | errorbox(){ 9 | #$1 is message 10 | #$2 is type: fatal or left blank 11 | if [ -z "$2" ];then 12 | yad --title='Pi Power Tools' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 13 | --text="Oh no!\n\n$1\nTry again?" \ 14 | --center --width=300 --text-align=center \ 15 | --button="OK!${DIRECTORY}/icons/forward.png:0" 2>/dev/null 16 | elif [ "$2" = 'fatal' ];then 17 | yad --title='Pi Power Tools' --window-icon="${DIRECTORY}/icons/logo-64.png" \ 18 | --text="Fatal error:\n$1\nClosing in 30 seconds." \ 19 | --center --width=300 --text-align=center \ 20 | --timeout=30 --timeout-indicator=bottom \ 21 | --button="Close!${DIRECTORY}/icons/exit.png:0" 2>/dev/null 22 | exit 1 23 | else 24 | errorbox 'errorbox error' fatal 25 | fi 26 | } 27 | 28 | ensurevalid(){ 29 | if [ -z "$output" ] || [ "$output" = "Device" ];then 30 | valid=0 31 | errorbox 'No valid device selected.' 32 | else #if not empty or Device, ensure valid usb device 33 | if [ ! -b "$output" ];then 34 | valid=0 35 | errorbox 'That USB device no longer exists. Likely you unplugged it.' 36 | else 37 | valid=1 38 | fi 39 | fi 40 | } 41 | 42 | while true;do 43 | 44 | (echo "# " 45 | sleep infinity)| yad --progress --pulsate --title="Scanning" \ 46 | --text="Scanning Devices..." --window-icon="${DIRECTORY}/icons/logo-64.png" \ 47 | --width=300 --height=100 --center --auto-close --auto-kill \ 48 | --no-buttons 2>/dev/null & 49 | child_pid=$! 50 | sleep 0.5 51 | parted_out="$(sudo parted -lm 2>/dev/null | grep '/dev/*')" 52 | kill $child_pid 53 | 54 | device_paths="$(echo $parted_out | tr ';' '\n' | tr -d ' ' | awk -F ":" '{print $1}')" 55 | device_sizes="$(echo $parted_out | tr ';' '\n' | awk -F ":" '{print $2}')" 56 | device_names="$(echo $parted_out | tr ';' '\n' | awk -F ":" '{print $7}')" 57 | 58 | #echo "$parted_out" 59 | #echo "$device_paths" 60 | #echo "$device_sizes" 61 | #echo "$device_names" 62 | 63 | device_list="Device\nSize\nName\n$(paste -d '\n' <(echo "$device_paths") <(echo "$device_sizes") <(echo "$device_names"))" 64 | #echo -e "$device_list" 65 | 66 | #if yad exits uncleanly it leaves behind a problematic memory file 67 | #this deletes it 68 | ipcrm -M 12345 &>/dev/null 69 | 70 | output="$(echo -e "$device_list" | yad --list --no-headers --no-click --separator='\n' \ 71 | --column=Device --column=Size --column=Name \ 72 | --image=${DIRECTORY}/icons/usb-64.png \ 73 | --print-column=1 --title='Pi Power Tools - USB Mode' \ 74 | --width=530 --height=260 --center --window-icon="${DIRECTORY}/icons/logo-64.png" \ 75 | --button=Home!"${DIRECTORY}/icons/home.png"!'Back to Home:5' \ 76 | --button=!"${DIRECTORY}/icons/refresh.png"!'Click this to refresh the USB Device list.:3' \ 77 | --button=Flash!"${DIRECTORY}/icons/burn.png"!'Copy everything from an img to a device.:6' \ 78 | --button=Boot!"${DIRECTORY}/icons/power.png"!'Boots the selected like a virtual machine.:2' \ 79 | --button=View!"${DIRECTORY}/icons/search.png"!'This lets you view the files.:0' \ 80 | --button=Edit!"${DIRECTORY}/icons/gparted.png"!'This edits the partitions using gparted.:4' \ 81 | --buttons-layout=end 2>/dev/null)" 82 | 83 | button=$? #get exit code to determine which button was pressed 84 | echo "$output" 85 | echo EOO 86 | #echo "$button" 87 | 88 | mntpnt="$(cat ${DIRECTORY}/data/mountpoint)" 89 | case $button in 90 | 91 | 1) 92 | echo "Error" 93 | exit 1 94 | ;; 95 | 96 | 2) 97 | echo "Boot" 98 | ensurevalid 99 | #the above command sets $valid to 1 if usb device exists. 100 | if [ $valid -eq 1 ];then 101 | "${DIRECTORY}/installgui" "systemd-container xserver-xephyr expect" &>/dev/null || exit 0 102 | "${DIRECTORY}/functions/terminal-run" "sudo ${DIRECTORY}/vdesktop/vdesktop $output $(cat "${DIRECTORY}/data/vdesktop.conf");sleep 10" "Boot console" 103 | #wait until vdesktop is closed before reopening UI 104 | sleep 10 105 | while ps -C vdesktop >/dev/null;do sleep 1; done 106 | fi 107 | ;; 108 | 109 | 0) 110 | echo "View" 111 | ensurevalid 112 | #the above command sets $valid to 1 if usb device exists. 113 | if [ $valid -eq 1 ];then 114 | BLKS="$(lsblk -rno PATH $output)" 115 | BOOT_PART="$(echo "$BLKS" | sed -n '2p')" 116 | ROOT_PART="$(echo "$BLKS" | sed -n '3p')" 117 | 118 | #get subdevices 119 | partitions="$(lsblk $output -no PATH | tail -n +2 | tr '\n' ' ')" 120 | echo "Subdevices: ${partitions}" 121 | #unmount subdevices twice in case one is a dependency of the other 122 | sudo umount $partitions &>/dev/null 123 | sudo umount $partitions &>/dev/null 124 | 125 | sudo rm -rf "$mntpnt" 126 | sudo mkdir "$mntpnt" || errorbox "Failed to create folder $mntpnt" fatal 127 | 128 | sudo mount -o rw --source $ROOT_PART --target "$mntpnt" 129 | sudo mount -o rw --source $BOOT_PART --target "${mntpnt}/boot" 130 | pcmanfm -n "$mntpnt" 131 | yad --title="Viewing $output" --window-icon="${DIRECTORY}/icons/logo-64.png" \ 132 | --text="Close this window when you are finished viewing the files." \ 133 | --center --text-align=center \ 134 | --button="Close!${DIRECTORY}/icons/exit.png:1" 2>/dev/null 135 | echo 'unmounting device' 136 | sudo umount -fl "${mntpnt}/boot" 137 | sudo umount -fl "$mntpnt" 138 | sudo rm -rf "$mntpnt" 139 | fi 140 | ;; 141 | 142 | 4) 143 | echo "Edit" 144 | ensurevalid 145 | #the above command sets $valid to 1 if usb device exists. 146 | if [ $valid -eq 1 ];then 147 | ${DIRECTORY}/installgui "gparted" || exit 0 148 | sudo gparted "$output" 149 | fi 150 | ;; 151 | 6) 152 | echo "Flash" 153 | "${DIRECTORY}/flash" "$output" 154 | ;; 155 | 3) 156 | echo "refresh" 157 | ;; 158 | 5) 159 | echo "home" 160 | "${DIRECTORY}/home" & 161 | exit 0 162 | ;; 163 | *) 164 | echo "unknown" 165 | exit 0 166 | ;; 167 | esac 168 | echo "" 169 | done 170 | --------------------------------------------------------------------------------