├── .github └── workflows │ └── docker-publish.yml ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README.md ├── bin ├── parseheaders.pl └── start.sh ├── etc └── apache.conf └── html ├── about.html ├── build.fcgi ├── build.ini ├── css └── ui.css ├── favicon.ico ├── images ├── forkme_right_red.png ├── html5-badge-h-css3-semantics.png ├── valid-html5-button.png └── vcss-blue.png ├── index.html └── js ├── jquery.bpopup.min.js └── ui.js /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | # This workflow uses actions that are not certified by GitHub. 4 | # They are provided by a third-party and are governed by 5 | # separate terms of service, privacy policy, and support 6 | # documentation. 7 | 8 | on: 9 | schedule: 10 | - cron: '44 13 * * *' 11 | push: 12 | branches: [ master ] 13 | # Publish semver tags as releases. 14 | tags: [ 'v*.*.*' ] 15 | pull_request: 16 | branches: [ master ] 17 | 18 | env: 19 | # Use docker.io for Docker Hub if empty 20 | REGISTRY: ghcr.io 21 | # github.repository as / 22 | IMAGE_NAME: ${{ github.repository }} 23 | 24 | 25 | jobs: 26 | build: 27 | 28 | runs-on: ubuntu-latest 29 | permissions: 30 | contents: read 31 | packages: write 32 | 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v2 36 | 37 | # Login against a Docker registry except on PR 38 | # https://github.com/docker/login-action 39 | - name: Log into registry ${{ env.REGISTRY }} 40 | if: github.event_name != 'pull_request' 41 | uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c 42 | with: 43 | registry: ${{ env.REGISTRY }} 44 | username: ${{ github.actor }} 45 | password: ${{ secrets.GITHUB_TOKEN }} 46 | 47 | # Extract metadata (tags, labels) for Docker 48 | # https://github.com/docker/metadata-action 49 | - name: Extract Docker metadata 50 | id: meta 51 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 52 | with: 53 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 54 | 55 | # Build and push Docker image with Buildx (don't push on PR) 56 | # https://github.com/docker/build-push-action 57 | - name: Build and push Docker image 58 | uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc 59 | with: 60 | context: . 61 | push: ${{ github.event_name != 'pull_request' }} 62 | tags: ${{ steps.meta.outputs.tags }} 63 | labels: ${{ steps.meta.outputs.labels }} 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | language: bash 5 | script: 6 | - docker version 7 | - docker build --rm=true --no-cache=true -t lps-rocks/rom-o-matic github.com/lps-rocks/rom-o-matic.git 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------ 2 | # Dynamic iPXE image generator 3 | # 4 | # Copyright (C) 2012-2019 Francois Lacroix. All Rights Reserved. 5 | # License: GNU General Public License version 3 or later; see LICENSE.txt 6 | # Website: http://ipxe.org, https://github.com/xbgmsharp/ipxe-buildweb 7 | #------------------------------------------------------------------------ 8 | # 9 | # Alpine Linux + Apache2 + module + my app 10 | # 11 | # Base from ultimate-seed Dockerfile 12 | # https://github.com/pilwon/ultimate-seed 13 | # 14 | # AUTHOR: xbgmsharp@gmail.com 15 | # WEBSITE: https://github.com/xbgmsharp/ipxe-buildweb 16 | # 17 | # DOCKER-VERSION 1.0.0 18 | # VERSION 0.0.1 19 | 20 | # Pull base image. 21 | FROM alpine 22 | MAINTAINER James DeVincentis 23 | 24 | # Upgrade existing base packages 25 | RUN apk --no-cache upgrade 26 | 27 | # Install System Dependencies 28 | RUN apk add --no-cache wget sudo bash 29 | 30 | # Install Compiling Dependencies 31 | RUN apk add --no-cache make gcc libc-dev zlib-dev openssl-dev xz-dev binutils-dev cdrkit syslinux 32 | 33 | # Perl and Perl Dependencies 34 | RUN apk add --no-cache perl perl-json perl-cgi perl-fcgi perl-uri perl-config-inifiles perl-ipc-system-simple perl-app-cpanminus 35 | RUN cpanm Sub::Override 36 | 37 | # Install git & jq 38 | RUN apk add --no-cache git jq 39 | 40 | # Install Apache2 41 | RUN apk add --no-cache apache2 apache-mod-fcgid 42 | 43 | # Configure Apache2 44 | ADD etc/apache.conf /etc/apache2/conf.d/rom-o-matic.conf 45 | RUN ln -s /dev/stderr /var/log/apache2/error.log 46 | RUN ln -s /dev/stdout /var/log/apache2/access.log 47 | 48 | # Install rom-o-matic WebUI 49 | ADD html/ /var/www/rom-o-matic/ 50 | 51 | # Copy parseheaders utility 52 | ADD bin/parseheaders.pl /opt/rom-o-matic/bin/ 53 | ADD bin/start.sh / 54 | RUN chmod +x /start.sh 55 | 56 | # Clean up CPAN 57 | RUN rm -Rf /root/.cpanm/ 58 | 59 | # Expose ports. 60 | EXPOSE 80 61 | 62 | # Launch apache as the server 63 | ENTRYPOINT ["/start.sh"] 64 | CMD ["httpd", "-D", "FOREGROUND"] 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | Awesome and simple movie downloader, for NZB and Torrents. 635 | Copyright (C) 2011 - Ruud Burger 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 | CouchPotato - Copyright (C) 2011 - Ruud Burger 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 | . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | iPXE Prebuilt binary web interface [![Build Status](https://travis-ci.com/lps-rocks/rom-o-matic.svg?branch=master)](https://travis-ci.com/lps-rocks/rom-o-matic) 2 | ===== 3 | 4 | ## Demo 5 | You can access a live version here: [rom-o-matic.dev](https://rom-o-matic.dev). The demo server is rebuilt nightly and will be hosted indefinitely. This project is attempting to revive previous domains, rom-o-matic.eu and rom-o-matic.net as well. 6 | 7 | ## Why 8 | A web interface for building iPXE binaries. Many users would prefer to be able to download prebuilt binary versions of iPXE, rather than building it from source. 9 | 10 | This interface is built on previous work here: [https://github.com/xbgmsharp/ipxe-buildweb](https://github.com/xbgmsharp/ipxe-buildweb) 11 | 12 | ## What 13 | A web-based user interface that provide a way for the user to select any relevant iPXE build options, specify any embedded script, etc, and then construct and download the appropriate file. 14 | 15 | ## How 16 | The user interface, is using HTML, CSS as well as Javascript (jQuery) and a suitable server-side language (Perl). 17 | All GUI options (git version/nics list/compile options) are generated from the iPXE source code dynamically. 18 | The build.fcgi script written in Perl by Michael Brown. 19 | 20 | ## Using GHCR image 21 | 22 | https://github.com/lps-rocks/rom-o-matic/pkgs/container/rom-o-matic 23 | 24 | * Supported tags: 25 | 26 | `master` - Latest ([Dockerfile](https://github.com/lps-rocks/rom-o-matic/blob/master/Dockerfile)) 27 | 28 | * Supported architectures: x86-64 29 | 30 | * Run rom-o-matic 31 | 32 | After a successful [Docker installation](https://docs.docker.com/engine/installation/) you just need to execute the following command in the shell: 33 | 34 | ```bash 35 | docker run -d \ 36 | --publish 8080:80 \ 37 | --name rom-o-matic \ 38 | ghcr.io/lps-rocks/rom-o-matic:master 39 | ``` 40 | 41 | ## Test using Docker 42 | 43 | * Install Docker 44 | [Install documentation of Docker](https://docs.docker.com/engine/installation/) 45 | 46 | The Docker deb package are valid for Ubuntu and Debian. 47 | 48 | ```bash 49 | $ wget http://get.docker.io/ -O - | sh 50 | ``` 51 | 52 | * Build the images 53 | 54 | The following command build the build directly from the github repository. 55 | 56 | The build process might take some time a while as it download the origin Ubuntu LTS 14.04 docker image. 57 | ```bash 58 | $ docker build --rm=true --no-cache=true -t lpsrocks/rom-o-matic github.com/lps-rocks/rom-o-matic.git 59 | ``` 60 | 61 | Alternatively, you can build the image localy after cloning the repository. 62 | ```bash 63 | $ docker build --rm=true --no-cache=true -t lpsrocks/rom-o-matic . 64 | ``` 65 | 66 | * Run the container 67 | 68 | Run as a detach container 69 | ```bash 70 | $ docker run -d -p 8080:80 -t lpsrocks/rom-o-matic 71 | ``` 72 | 73 | Or run the container with an attach shell 74 | ``` 75 | $ docker run -i --rm -p 22:22 -p 8080:80 -t lpsrocks/rom-o-matic /bin/bash 76 | ``` 77 | 78 | * Check the IP 79 | 80 | ```bash 81 | $ docker ps -a 82 | $ docker inspect CONTAINER_ID | grep IPA 83 | ``` 84 | 85 | Or both command in one 86 | ```bash 87 | $ docker ps -a | grep ipxe-buildweb | awk '{print $1}' | xargs docker inspect | grep IPAddress 88 | ``` 89 | 90 | Or all in one with the ssh connection 91 | ```bash 92 | $ ssh $(docker ps -a | grep ipxe-buildweb | awk '{print $1}' | xargs docker inspect | grep IPAddress | awk '{print $2}' | tr -d '"' | tr -d ',' ) 93 | ``` 94 | 95 | * Login in the container via SSH 96 | 97 | User is root and password is admin. 98 | 99 | ```bash 100 | $ ssh root@172.17.0.x 101 | ``` 102 | 103 | * Review logs 104 | 105 | ```bash 106 | $ docker logs CONTAINER_ID 107 | ``` 108 | 109 | * Enjoy! 110 | 111 | ## Contributing 112 | 113 | 1. Fork it 114 | 2. Create a branch (`git checkout -b my_markup`) 115 | 3. Commit your changes (`git commit -am "Added Snarkdown"`) 116 | 4. Push to the branch (`git push origin my_markup`) 117 | 5. Create an [Issue][1] with a link to your branch 118 | 6. Or Send me a [Pull Request][2] 119 | 120 | [1]: https://github.com/lpsrocks/rom-o-matic/issues 121 | [2]: https://github.com/lpsrocks/rom-o-matic/pull/new/master 122 | 123 | ## License 124 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 125 | -------------------------------------------------------------------------------- /bin/parseheaders.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # 3 | # Generates list of options from header file 4 | # 5 | # Initial version by Francois Lacroix 6 | #------------------------------------------------------------------------ 7 | # Dynamic iPXE image generator 8 | # 9 | # Copyright (C) 2012-2019 Francois Lacroix. All Rights Reserved. 10 | # License: GNU General Public License version 3 or later; see LICENSE.txt 11 | # Website: http://ipxe.org, https://github.com/xbgmsharp/ipxe-buildweb 12 | #------------------------------------------------------------------------ 13 | ### Dependencies 14 | # apt-get install libjson-perl 15 | # or 16 | # perl -MCPAN -e 'install JSON' 17 | ### Install 18 | # Copy the script into the ipxe source eg: /opt/ipxe/src/util/ 19 | ### Run 20 | # The script is run by options.php 21 | 22 | use strict; 23 | use warnings; 24 | use autodie; 25 | use v5.10; 26 | #use Data::Dumper; 27 | use JSON; 28 | 29 | my $bool; # list of define value 30 | #$def{bool} = \@bool; 31 | 32 | my $directory = '/tmp/ipxe/source/src/config'; 33 | opendir (DIR, $directory) or die $!; 34 | while (my $file = readdir(DIR)) 35 | { 36 | next if ($file =~ m/^\./); 37 | next unless ($file =~ m/.h$/); 38 | next if ($file =~ m/colour/); # File we skip 39 | #print $file . "\n"; 40 | #my $file = "general.h"; 41 | open (FILE, $directory."/".$file); 42 | while (my $line = ) { 43 | chomp($line); 44 | # skip blank lines 45 | next if ($line =~ m/^$/); 46 | if ($line =~ /#define/) 47 | { 48 | #print $line . "\n"; 49 | if ($line =~ /([a-zA-Z_\/\/\#]*)(\t+|\s+)(\w*)\t+(\W+)([a-zA-Z0-9_\-\'\:\=\,\>\(\)\!\/ ]+)/g) 50 | { 51 | #print "----------Found in 2\n"; 52 | #print "1 - $1\n"; 53 | #print "3 - $3\n"; 54 | #print "5 - $5\n"; 55 | my $type = $1; 56 | my $name = $3; 57 | my $desc = $5; 58 | if ($type =~ /define/) 59 | { 60 | if ($type =~ /^\/\/\#/) # If comment then undef 61 | { 62 | #print "Add bool undef-------------------------------\n"; 63 | push(@$bool, {file=> $file, type => "undef", name => $name, description => $desc}); 64 | } else { 65 | #print "Add bool define-------------------------------\n"; 66 | push(@$bool, {file=> $file, type => "define", name => $name, description => $desc}); 67 | } 68 | } 69 | if ($type !~ /define/) 70 | { 71 | #print "Add input-------------------------------\n"; 72 | push(@$bool, {file=> $file, type => "input", name => $type, value => $name, description => $desc}); 73 | } 74 | } 75 | elsif ($line =~ /([a-zA-Z_]*)(\t+|\s+)([a-zA-Z0-9\:\/\"\.\% ]+)$/g) 76 | { 77 | #print "----------Found in 1\n"; 78 | #print "1 - $1\n"; 79 | #print "3 - $3\n"; 80 | push(@$bool, {file=> $file, type => "input", name => $1, value => $3, description => $1}); 81 | } 82 | 83 | } 84 | if ($line =~ /#undef/) 85 | { 86 | #print $line . "\n"; 87 | if ($line =~ /([a-zA-Z]*)(\t+|\s+)(\w*)\t+(\W+)([a-zA-Z0-9_\- ]+)/g) 88 | { 89 | #print "1 - $1\n"; 90 | #print "3 - $3\n"; 91 | #print "5 - $5\n"; 92 | push(@$bool, {file=> $file, type => $1, name => $3, description => $5}); 93 | if ($1 !~ /undef/) 94 | { 95 | #print "to FIXE-------------------------------\n"; 96 | pop(@$bool); 97 | } 98 | } 99 | } 100 | } 101 | close (FILE); 102 | } 103 | closedir(DIR); 104 | 105 | #print Dumper $bool; 106 | #foreach my $options ( @$bool ) { 107 | # print "$options->{'name'}\t$options->{'description'}\n"; 108 | #} 109 | 110 | print JSON->new->pretty->utf8->encode(\@$bool); 111 | 112 | exit; 113 | -------------------------------------------------------------------------------- /bin/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Create temporary directories 4 | mkdir -p /tmp/ipxe/build 5 | mkdir -p /tmp/ipxe/cache 6 | 7 | # Clean temporary directories 8 | rm -Rf /tmp/ipxe/build/* 9 | rm -Rf /tmp/ipxe/cache/* 10 | 11 | # Clone iPXE into temporary source as apache 12 | if [[ ! -d /tmp/ipxe/source ]]; then 13 | git clone https://github.com/ipxe/ipxe.git /tmp/ipxe/source 14 | while [ $? -ne 0 ]; do 15 | git clone https://github.com/ipxe/ipxe.git /tmp/ipxe/source 16 | sleep 15 17 | done 18 | else 19 | cd /tmp/ipxe/source 20 | while [ $? -ne 0 ]; do 21 | git pull 22 | sleep 15 23 | done 24 | fi 25 | 26 | # Change owner of files 27 | chown apache:apache -Rf /tmp/ipxe/ 28 | 29 | # Build Cache files 30 | cd /tmp/ipxe/source/src/ 31 | perl /opt/rom-o-matic/bin/parseheaders.pl > /var/www/rom-o-matic/options.json 32 | perl /tmp/ipxe/source/src/util/niclist.pl --format json --columns ipxe_name,device_id,vendor_id > /var/www/rom-o-matic/nics.json 33 | git rev-list --all --max-count=20 --abbrev-commit --abbrev=1 | jq -R -s -c 'split("\n")' > /var/www/rom-o-matic/versions.json 34 | 35 | # Launch Apache 36 | exec "$@" 37 | -------------------------------------------------------------------------------- /etc/apache.conf: -------------------------------------------------------------------------------- 1 | # Security Settings 2 | ServerTokens prod 3 | 4 | # Rom-O-Matic virtaul host 5 | 6 | ServerAdmin webmaster@localhost 7 | DocumentRoot /var/www/rom-o-matic 8 | 9 | # Redirect logging 10 | ErrorLog /dev/stderr 11 | CustomLog /dev/stdout combined 12 | 13 | # Make FCGID script work 14 | AddHandler fcgid-script .fcgi 15 | 16 | # FCGID Tuning 17 | FcgidConnectTimeout 120 18 | FcgidIdleTimeout 3600 19 | FcgidBusyTimeout 300 20 | FcgidIOTimeout 360 21 | FcgidMaxRequestLen 15728640 22 | 23 | # Permit Access 24 | 25 | Options +ExecCGI 26 | Require all granted 27 | 28 | 29 | -------------------------------------------------------------------------------- /html/about.html: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | About 17 | 18 | 19 |

iPXE web builder

20 |

Why

21 | A Prebuilt binary web interface. 22 | Many users would prefer to be able to download prebuilt binary versions of iPXE, rather than building it from source.
23 |

Souce

24 | Source are available on Github.
25 | Comments, questions, suggestions and contribution are welcome.
26 | Please fork and send a pull request or you can create an issue. 27 |

License

28 |

Copyright (C) 2012-2019 Francois Lacroix. All Rights Reserved.

29 |

This program is free software; you can redistribute it and/or modify 30 | it under the terms of the GNU General Public License 31 | as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
32 |

33 | 34 | 35 | -------------------------------------------------------------------------------- /html/build.fcgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # Build ipxe binary according to options 4 | # 5 | # Initial version by Michael Brown 6 | # Modified version by Francois Lacroix 7 | # Current version by James DeVincentis 8 | #------------------------------------------------------------------------ 9 | # Dynamic iPXE image generator 10 | # 11 | # Copyright (C) 2020 James DeVincentis. Based on the work of Francois Lacroix 12 | # Copyright (C) 2012-2019 Francois Lacroix. All Rights Reserved. 13 | # License: GNU General Public License version 3 or later; see LICENSE.txt 14 | # Website: http://ipxe.org, https://github.com/xbgmsharp/ipxe-buildweb 15 | # https://github.com/lps-rocks/rom-o-matic 16 | #------------------------------------------------------------------------ 17 | ### Dependencies 18 | # apt-get install liburi-perl libfcgi-perl libconfig-inifiles-perl libipc-system-simple-perl libsub-override-perl 19 | # mkdir /var/cache/ipxe-build /var/run/ipxe-build /var/tmp/ipxe-build 20 | # rm -rf /var/cache/ipxe-build/* /var/run/ipxe-build/* /var/tmp/ipxe-build/* 21 | # apt-get install git-core 22 | # cd /var/tmp/ && rm -rf ipxe && git clone https://git.ipxe.org/ipxe.git 23 | # touch /var/run/ipxe-build/ipxe-build-cache.lock 24 | # chown -R www-data:www-data /var/run/ipxe-build/ipxe-build-cache.lock /var/cache/ipxe-build /var/run/ipxe-build /var/tmp/ipxe-build /var/tmp/ipxe 25 | ### Apache 26 | # apt-get install libapache2-mod-fcgid && a2enmod fcgid 27 | # 28 | # AllowOverride None 29 | # AddHandler fcgid-script .fcgi 30 | # Options +ExecCGI +Includes +FollowSymLinks 31 | # Order allow,deny 32 | # Allow from all 33 | # 34 | ### Sample usage 35 | # $ perl build.fcgi BINARY=ipxe.iso BINDIR=bin REVISION=master DEBUG= EMBED.00script.ipxe='%23!ipxe%0Aecho "Dynamic iPXE image generator"%0Adhcp%0Aconfig%0A%0A%0A' \ 36 | # general.h/VLAN_CMD:=1 general.h/REBOOT_CMD:=1 general.h/PRODUCT_NAME=Build general.h/PRODUCT_SHORT_NAME=iPXE3.0 general.h/BANNER_TIMEOUT=90 37 | 38 | use CGI qw ( :cgi ); 39 | use FCGI; 40 | use URI; 41 | use Getopt::Long; 42 | use Config::IniFiles; 43 | use File::Temp; 44 | use File::Spec::Functions qw ( tmpdir catdir catfile splitpath ); 45 | use File::Find; 46 | use File::stat; 47 | use Fcntl qw ( :flock ); 48 | use IO::File; 49 | use IO::Seekable; 50 | use IO::Compress::Gzip qw ( gzip $GzipError ); 51 | use IPC::System::Simple qw ( systemx capturex ); 52 | use POSIX qw ( nice strftime ); 53 | use Sub::Override; 54 | use strict; 55 | use warnings; 56 | 57 | # Disable warning, not applicable here 58 | $CGI::LIST_CONTEXT_WARN = 0; 59 | 60 | # Parse command line options 61 | my $verbosity = 2; 62 | my $cfgfile = "build.ini"; 63 | my $foreground = 0; 64 | my $keep; 65 | Getopt::Long::Configure ( "bundling", "auto_abbrev" ); 66 | GetOptions ( 67 | "foreground|f" => sub { $foreground = 1; }, 68 | "verbose|v+" => sub { $verbosity++; }, 69 | "quiet|q+" => sub { $verbosity--; }, 70 | "config|c=s" => sub { shift; $cfgfile = shift; }, 71 | "keep|k" => sub { $keep = 1; }, 72 | ) or die "Could not parse command-line options\n"; 73 | 74 | # Load configuration 75 | my $cfg = Config::IniFiles->new ( -file => $cfgfile ) 76 | or die join ( "\n", @Config::IniFiles::errors )."\n"; 77 | my $repository = $cfg->val ( "source", "repository" ) 78 | or die "No repository specified in ".$cfgfile."\n"; 79 | my $repository_git = "${repository}/.git"; 80 | my $niceness = $cfg->val ( "build", "niceness", 0 ); 81 | my $concurrency = $cfg->val ( "build", "concurrency" ); 82 | my $bindirs = $cfg->val ( "build", "bindirs", "bin" ); 83 | $bindirs = { map { $_ => 1 } split ( /\s+/, $bindirs ) }; 84 | my $tmpdir = $cfg->val ( "build", "tmpdir", tmpdir() ); 85 | my $cacheroot = $cfg->val ( "cache", "root" ) 86 | or die "No cache root specified in ".$cfgfile."\n"; 87 | die "Cache root \"".$cacheroot."\" does not exist\n" unless -d $cacheroot; 88 | my $lockfile = $cfg->val ( "cache", "lockfile" ) 89 | or die "No lockfile specified in ".$cfgfile."\n"; 90 | open my $lockfh, "+>>", $lockfile 91 | or die "Could not open lockfile: $!\n"; 92 | my $cachemax = $cfg->val ( "cache", "max", 20 ); 93 | 94 | # Duplicate original STDIN, STDOUT and STDERR 95 | open my $origstdin, "<&", \*STDIN or die "Could not dup STDIN: $!\n"; 96 | open my $origstdout, ">&", \*STDOUT or die "Could not dup STDOUT: $!\n"; 97 | open my $origstderr, ">&", \*STDERR or die "Could not dup STDERR: $!\n"; 98 | 99 | ############################################################################### 100 | # 101 | # Manage cache lock 102 | # 103 | 104 | sub cache_lock { 105 | flock ( $lockfh, LOCK_EX ) or die "Cannot lock cache: $!\n"; 106 | } 107 | 108 | sub cache_unlock { 109 | flock ( $lockfh, LOCK_UN ) or die "Cannot unlock cache: $!\n"; 110 | } 111 | 112 | ############################################################################### 113 | # 114 | # Load best available cached binaries 115 | # 116 | 117 | sub load_cached_binaries { 118 | my $gitdir = shift; 119 | my $worktree = shift; 120 | my $revision = shift; 121 | my $bindir = shift; 122 | 123 | warn "Finding closest cached binaries for ".$revision."...\n" 124 | if $verbosity > 1; 125 | my $best_tag; 126 | my $best_distance; 127 | 128 | # Obtain cache lock 129 | cache_lock(); 130 | 131 | # Find closest source before specified revision 132 | my $before; 133 | eval { 134 | $before = capturex ( "git", "--git-dir", $gitdir, "--work-tree", $worktree, 135 | "describe", "--long", "--tags", "--match", 136 | "ipxe-build/cached/".$bindir."/*", $revision ); 137 | chomp $before; 138 | }; 139 | if ( $before ) { 140 | ( my $tag, my $distance ) = ( $before =~ /^(.+)-(\d+)-g[0-9a-f]+$/ ) 141 | or die "Invalid git description \"".$before."\"\n"; 142 | $best_distance = $distance; 143 | $best_tag = $tag; 144 | } 145 | 146 | # Find closest source after specified revision 147 | my $after; 148 | eval { 149 | $after = capturex ( "git", "--git-dir", $gitdir, "--work-tree", $worktree, 150 | "describe", "--long", "--tags", "--contains", "--match", 151 | "ipxe-build/cached/".$bindir."/*", $revision ); 152 | chomp $after; 153 | }; 154 | if ( $after ) { 155 | ( my $tag, undef, my $distance ) = ( $after =~ /^(.+?)(~(\d+))?$/ ) 156 | or die "Invalid git description \"".$after."\"\n"; 157 | $distance ||= 0; 158 | if ( ( ! defined $best_distance ) || ( $distance < $best_distance ) ) { 159 | $best_distance = -$distance; 160 | $best_tag = $tag; 161 | } 162 | } 163 | 164 | # Do nothing if we have no cached binaries available 165 | if ( ! $best_tag ) { 166 | warn "Found no cached binaries\n" if $verbosity > 0; 167 | cache_unlock(); 168 | return; 169 | } 170 | warn "Found cached binaries in ".$best_tag." at distance " 171 | .$best_distance."\n" if $verbosity > 0; 172 | 173 | # Identify revision with cached binaries 174 | ( my $cached, my $suffix ) = ( $best_tag =~ /\/([0-9a-f]+)(\.gz)?$/ ) 175 | or die "Invalid ipxe-build/cached tag \"".$best_tag."\"\n"; 176 | $suffix ||= ""; 177 | 178 | # Open binary tarball (compressed or otherwise). 179 | my $tarball = catfile ( $cacheroot, $cached."-".$bindir.".tar".$suffix ); 180 | warn "Opening binary tarball ".$tarball."...\n" if $verbosity > 1; 181 | my $tarfh; 182 | open $tarfh, "<", $tarball; 183 | if ( ! $tarfh ) { 184 | warn "Could not open ".$tarball.": $! - Skipping Cache.\n" if $verbosity > 0; 185 | cache_unlock(); 186 | return; 187 | } 188 | warn "Opened binary tarball ".$tarball."...\n" if $verbosity > 1; 189 | 190 | # Update tarball's timestamp if it is an exact match 191 | utime undef, undef, $tarfh if $cached eq $revision; 192 | 193 | # Release cache lock 194 | cache_unlock(); 195 | 196 | # Check out commit corresponding to cached binaries. Check out by 197 | # sha1 rather than tag, since we have released the cache lock and so 198 | # the tag may no longer exist. 199 | warn "Checking out revision ".$cached."...\n" if $verbosity > 1; 200 | systemx ( "git", "--git-dir", $gitdir, "--work-tree", $worktree, 201 | "checkout", "--quiet", $cached ); 202 | 203 | # Identify commit timestamp. This is a timestamp that must 204 | # logically be older than any file in the corresponding binary 205 | # tarball. 206 | warn "Identifying timestamp for commit ".$cached."...\n" if $verbosity > 1; 207 | my $timestamp = capturex ( "git", "--git-dir", $gitdir, "--work-tree", 208 | $worktree, "show", "-s", "--format=\%ct", 209 | $cached ); 210 | chomp $timestamp; 211 | 212 | # Set timestamps on all checked-out files to the commit timestamp 213 | warn "Setting timestamps to ".strftime ( "%c", localtime ( $timestamp ) ) 214 | ."...\n" if $verbosity > 1; 215 | find ( { wanted => sub { utime $timestamp, $timestamp, $_; }, 216 | no_chdir => 1 }, $worktree ); 217 | 218 | # Unpack binary tarball 219 | warn "Unpacking binary tarball ".$tarball."...\n" if $verbosity > 1; 220 | close STDIN; 221 | open STDIN, "<&", $tarfh or die "Could not dup tarfh: $!\n"; 222 | systemx ( "tar", "-x", "-C", catdir ( $worktree ), 223 | ( $suffix ? ( "-z" ) : () ) ); 224 | close $tarfh; 225 | close STDIN; 226 | open STDIN, "<&", $origstdin or die "Could not restore STDIN: $!\n"; 227 | 228 | return $cached; 229 | } 230 | 231 | ############################################################################### 232 | # 233 | # Save cached binaries 234 | # 235 | 236 | sub save_cached_binaries { 237 | my $gitdir = shift; 238 | my $worktree = shift; 239 | my $revision = shift; 240 | my $bindir = shift; 241 | 242 | # Build blib.a 243 | warn "Building cacheable binaries...\n" if $verbosity > 1; 244 | systemx ( "make", "-C", catdir ( $worktree, "src" ), 245 | ( $concurrency ? ( "-j", $concurrency ) : () ), 246 | catfile ( $bindir, "blib.a" ) ); 247 | 248 | # Generate file list 249 | warn "Generating binary file list...\n" if $verbosity > 1; 250 | my @files = capturex ( "git", "--git-dir", $gitdir, "--work-tree", $worktree, 251 | "ls-files", "--others" ); 252 | chomp @files; 253 | 254 | # Create tarball 255 | my $tarball = catfile ( $cacheroot, $revision."-".$bindir.".tar" ); 256 | warn "Creating binary tarball ".$tarball."...\n" if $verbosity > 1; 257 | my $tarfh = File::Temp->new ( TEMPLATE => "ipxe-cache-XXXXXX", 258 | DIR => $cacheroot ); 259 | chmod 0664, $tarfh or die "Could not set permissions: $!\n"; 260 | systemx ( "tar", "-c", "-C", catdir ( $worktree ), 261 | "-f", $tarfh->filename, @files ); 262 | 263 | # Obtain cache lock 264 | cache_lock(); 265 | 266 | # Move tarball into position 267 | if ( rename ( $tarfh->filename, $tarball ) ) { 268 | $tarfh->unlink_on_destroy ( 0 ); 269 | } else { 270 | # Tarball has already been created concurrently - harmless waste 271 | warn "Could not create binary tarball ".$tarball.": $!\n"; 272 | } 273 | undef $tarfh; 274 | 275 | # Create tag in upstream repository. Do this even if tarball 276 | # already exists, in case a previous cache save managed to create 277 | # the tarball but failed to create the tag. (There is no way to 278 | # make these two operations properly atomic.) 279 | my $tag = "ipxe-build/cached/".$bindir."/".$revision; 280 | warn "Creating tag ".$tag."...\n" if $verbosity > 1; 281 | systemx ( "git", "--git-dir", $repository_git, "tag", "--force", 282 | $tag, $revision ); 283 | 284 | # Obtain list of all cached binaries 285 | warn "Listing all cache tags...\n" if $verbosity > 1; 286 | my @candidates = map { chomp; ( $_ eq $tag ) ? () : ( { tag => $_ } ) } 287 | capturex ( "git", "--git-dir", $repository_git, "tag", "-l", 288 | "ipxe-build/cached/*" ); 289 | 290 | # Find modification times for candidates 291 | foreach my $candidate ( @candidates ) { 292 | ( $candidate->{bindir}, $candidate->{revision}, my $suffix ) = 293 | ( $candidate->{tag} =~ 294 | /^ipxe-build\/cached\/(.+?)\/([0-9a-f]+)(\.gz)?$/ ) 295 | or die "Invalid tag name \"".$candidate->{tag}."\"\n"; 296 | $suffix ||= ""; 297 | $candidate->{tarball} = catfile ( $cacheroot, $candidate->{revision}."-". 298 | $candidate->{bindir}.".tar".$suffix ); 299 | my $stat = stat ( $candidate->{tarball} ); 300 | if ( $stat ) { 301 | $candidate->{mtime} = $stat->mtime; 302 | } else { 303 | warn "Missing tarball ".$candidate->{tarball}."\n"; 304 | $candidate->{mtime} = 0; # Treat as very old to force tag removal 305 | } 306 | } 307 | @candidates = sort { $a->{mtime} <=> $b->{mtime} } @candidates; 308 | 309 | # Expire oldest candidates to reduce cache size 310 | while ( @candidates >= $cachemax ) { 311 | my $candidate = shift @candidates; 312 | 313 | # Delete tag 314 | warn "Deleting tag ".$candidate->{tag}."...\n" if $verbosity > 1; 315 | systemx ( "git", "--git-dir", $repository_git, "tag", "-d", $candidate->{tag} ); 316 | 317 | # Delete tarball 318 | warn "Deleting binary tarball ".$candidate->{tarball}."...\n" 319 | if $verbosity > 1; 320 | unlink ( $candidate->{tarball} ); 321 | } 322 | 323 | # Start gzip running in background. We leave this running when we 324 | # exit. 325 | start_gzip ( $revision, $bindir ); 326 | 327 | # Release cache lock 328 | cache_unlock(); 329 | } 330 | 331 | ############################################################################### 332 | # 333 | # Run gzip in background 334 | # 335 | # We use gzip rather than bzip2 because the *decompression* speed for 336 | # gzip is almost an order of magnitude faster than for bzip2. 337 | # 338 | 339 | sub start_gzip { 340 | my $revision = shift; 341 | my $bindir = shift; 342 | 343 | # Open tarball for reading 344 | my $tarball = catfile ( $cacheroot, $revision."-".$bindir.".tar" ); 345 | warn "Compressing binary tarball ".$tarball."...\n" if $verbosity > 1; 346 | open my $tarfh, "<", $tarball 347 | or die "Could not open ".$tarball.": $!\n"; 348 | 349 | # Fork child process 350 | my $child = fork(); 351 | if ( ! defined $child ) { 352 | die "Could not fork: $!\n"; 353 | } elsif ( $child ) { 354 | close $tarfh; 355 | return; 356 | } 357 | 358 | # Open temporary file 359 | my $gzfh = File::Temp->new ( TEMPLATE => "ipxe-gzip-XXXXXX", 360 | DIR => $cacheroot ); 361 | chmod 0664, $gzfh or die "Could not set permissions: $!\n"; 362 | 363 | # Restore original STDIN, STDOUT and STDERR so that there's 364 | # somewhere for error messages to go after parent exits 365 | close STDIN; 366 | open STDIN, "<&", $origstdin or die "Could not restore STDIN: $!\n"; 367 | close STDOUT; 368 | open STDOUT, ">&", $origstdout or die "Could not restore STDOUT: $!\n"; 369 | close STDERR; 370 | open STDERR, ">&", $origstderr or die "Could not restore STDERR: $!\n"; 371 | 372 | # Compress to temporary file 373 | gzip $tarfh => $gzfh or die "Compression failed: ".$GzipError."\n"; 374 | 375 | # Obtain cache lock 376 | cache_lock(); 377 | 378 | # Move compressed tarball into position 379 | my $tarball_gz = $tarball.".gz"; 380 | if ( rename ( $gzfh->filename, $tarball_gz ) ) { 381 | $gzfh->unlink_on_destroy ( 0 ); 382 | } else { 383 | # Compressed tarball has already been created concurrently - harmless waste 384 | warn "Could not create compressed binary tarball ".$tarball_gz.": $!\n"; 385 | } 386 | undef $gzfh; 387 | 388 | # Create tag in upstream repository. As before, do this even if 389 | # compressed tarball already exists. 390 | my $gztag = "ipxe-build/cached/".$bindir."/".$revision.".gz"; 391 | warn "Creating tag ".$gztag."...\n" if $verbosity > 2; 392 | systemx ( "git", "--git-dir", $repository_git, "tag", "--force", 393 | $gztag, $revision ); 394 | 395 | # Delete tag for uncompressed tarball. Allow for the tag to have 396 | # already been deleted, since we released the cache lock while 397 | # performing compression. 398 | my $tag = "ipxe-build/cached/".$bindir."/".$revision; 399 | warn "Deleting tag ".$tag."...\n" if $verbosity > 2; 400 | eval { 401 | systemx ( "git", "--git-dir", $repository_git, "tag", "-d", $tag ); 402 | }; 403 | if ( $@ ) { 404 | warn "Could not delete tag ".$tag.": $@\n"; 405 | } 406 | 407 | # Delete uncompressed tarball, if it still exists 408 | unlink ( $tarball ); 409 | 410 | # Release cache lock 411 | cache_unlock(); 412 | 413 | # Exit child 414 | exit ( 0 ); 415 | } 416 | 417 | ############################################################################### 418 | # 419 | # Generate config/local/*.h 420 | # 421 | 422 | sub config_local { 423 | my $worktree = shift; 424 | my $params = shift; 425 | 426 | # Parse parameters to determine file content 427 | my $headers = {}; 428 | while ( ( my $key, my $value ) = each %$params ) { 429 | ( my $header, my $define, my $boolean ) = 430 | ( $key =~ /^(\w+\.h)\/(\w+)(:)?$/ ) 431 | or die "Invalid header/definition pair \"".$key."\"\n"; 432 | next if $value eq ""; 433 | my $line; 434 | if ( $boolean ) { 435 | if ( $value ) { 436 | $line = "#define ".$define; 437 | } else { 438 | $line = "#undef ".$define; 439 | } 440 | } else { 441 | if ( $value =~ /^[a-zA-Z0-9_\. ]+$/ ) { 442 | $line = "#undef ".$define."\n"; 443 | if ($define =~ /PRODUCT/) { 444 | $line .= "#define ".$define." \"".$value."\""; 445 | } else { 446 | $line .= "#define ".$define." ".$value; 447 | } 448 | } else { 449 | die "Invalid definition value \"".$value."\" for \"".$define."\"\n"; 450 | } 451 | } 452 | $headers->{$header} ||= ""; 453 | $headers->{$header} .= $line."\n"; 454 | } 455 | 456 | # Generate files 457 | while ( ( my $header, my $content ) = each %$headers ) { 458 | 459 | warn "Local configuration for ".$header.":\n".$content 460 | if $verbosity > 1; 461 | 462 | my $file = catfile ( $worktree, "src", "config", "local", $header ); 463 | open my $fh, "+<", $file 464 | or die "Could not open ".$file.": $!\n"; 465 | print $fh $content; 466 | close $fh; 467 | } 468 | } 469 | 470 | ############################################################################### 471 | # 472 | # Handle files uploaded to be embedded 473 | # 474 | 475 | sub embed { 476 | my $cgi = shift; 477 | my $params = shift; 478 | 479 | # Retrieve list of uploaded files 480 | my @files; 481 | foreach my $param ( sort grep { /^EMBED/ } keys %$params ) { 482 | ( undef, my $name ) = ( $param =~ /^EMBED(\.(.+))?$/ ) 483 | or die "Invalid EMBED* parameter name \"".$param."\"\n"; 484 | foreach my $value ( $cgi->multi_param ( $param ) ) { 485 | next unless $value; 486 | my $tempfile = $cgi->tmpFileName ( $value ); 487 | if ( $tempfile ) { 488 | # Value is the local filename 489 | push @files, { 490 | name => $value, 491 | tempfile => $tempfile, 492 | }; 493 | } else { 494 | # Value is the literal content 495 | push @files, { 496 | name => $name, 497 | content => $value, 498 | }; 499 | } 500 | } 501 | delete $params->{$param}; 502 | } 503 | 504 | # Do nothing if no files were uploaded 505 | return unless @files; 506 | 507 | # Create directory for upload symlinks and literal content temporary files 508 | my $embeddirfh = File::Temp->newdir ( "ipxe-embed-XXXXXX", DIR => $tmpdir, 509 | CLEANUP => ! $keep ); 510 | my $embeddir = $embeddirfh->dirname; 511 | warn "Temporary embedded image directory: ".$embeddir."\n" if $verbosity > 0; 512 | 513 | # Create upload symlinks and EMBED list 514 | my @embed; 515 | foreach my $file ( @files ) { 516 | 517 | # Determine embedded filename 518 | ( undef, undef, my $filename ) = splitpath ( $file->{name} ); 519 | die "Invalid embedded image filename \"".$filename."\"\n" 520 | unless $filename =~ /^\w[\w\.]*$/; 521 | my $path = catfile ( $embeddir, $filename ); 522 | warn "Embedded image: ".$path."\n" if $verbosity > 0; 523 | 524 | # Create symlink with appropriate name 525 | if ( $file->{tempfile} ) { 526 | symlink ( $file->{tempfile}, $path ) 527 | or die "Could not symlink ".$path." to ".$file->{tempfile}.": $!\n"; 528 | } else { 529 | warn $file->{content}."\n" if $verbosity > 0; 530 | open my $fh, ">", $path 531 | or die "Could not create ".$path.": $!\n"; 532 | print $fh $file->{content}; 533 | close $fh; 534 | } 535 | 536 | # Add EMBED list entry 537 | push @embed, $path; 538 | } 539 | 540 | return ( join ( ",", @embed ), $embeddirfh ); 541 | } 542 | 543 | ############################################################################### 544 | # 545 | # Handle build request 546 | # 547 | 548 | sub build { 549 | my $cgi = shift; 550 | 551 | # Parse URI 552 | my $path_info = $cgi->path_info(); 553 | my $params = $cgi->Vars; 554 | if ( $verbosity > 1 ) { 555 | warn "Path: ".$path_info."\n"; 556 | warn "Parameters: \n"; 557 | foreach my $key ( sort keys %$params ) { 558 | warn " ".$key." = ".$params->{$key}."\n"; 559 | } 560 | } 561 | my $segments = [ URI->new ( $path_info )->path_segments ]; 562 | my $binary = ( pop @$segments || $params->{BINARY} ); 563 | delete $params->{BINARY}; 564 | die "No binary specified\n" unless $binary; 565 | warn "Binary: ".$binary."\n" if $verbosity > 0; 566 | my $bindir = ( ( scalar @$segments && ( $segments->[-1] =~ /^bin(-|$)/ ) ) ? 567 | pop @$segments : ( $params->{BINDIR} || "bin" ) ); 568 | delete $params->{BINDIR}; 569 | warn "Binary directory: ".$bindir."\n" if $verbosity > 0; 570 | my $revision = ( join ( "/", grep { $_ } @$segments ) || 571 | $params->{REVISION} || "HEAD" ); 572 | delete $params->{REVISION}; 573 | warn "Revision: ".$revision."\n" if $verbosity > 0; 574 | 575 | # Check final binary name 576 | $binary =~ /^\w[\w-]*\.[a-z]+$/ 577 | or die "Invalid binary name \"".$binary."\"\n"; 578 | 579 | # Check binary directory 580 | die "Invalid binary directory \"".$bindir."\"\n" 581 | unless exists $bindirs->{$bindir}; 582 | 583 | # Check revision 584 | $revision =~ /^\w\S+$/ 585 | or die "Invalid revision \"".$revision."\"\n"; 586 | 587 | # Parse DEBUG, if present 588 | my $debug = $params->{DEBUG}; 589 | undef $debug if $debug eq ""; 590 | if ( $debug ) { 591 | die "Invalid DEBUG \"".$debug."\"\n" 592 | unless $debug =~ /^(\w+(:\d+)?(,\w+(:\d+)?)*)?$/; 593 | warn "DEBUG: ".$debug."\n" if $verbosity > 0; 594 | } 595 | delete $params->{DEBUG}; 596 | 597 | # Parse EMBED, if present 598 | ( my $embed, my $embeddirfh ) = embed ( $cgi, $params ); 599 | 600 | # Canonicalise git revision 601 | warn "Canonicalising revision ".$revision."...\n" if $verbosity > 1; 602 | $revision = capturex ( "git", "--git-dir", $repository_git, "rev-parse", 603 | "--verify", $revision ); 604 | chomp $revision; 605 | warn "Canonical revision: ".$revision."\n" if $verbosity > 0; 606 | 607 | # Create temporary directories 608 | warn "Creating temporary directories...\n" if $verbosity > 1; 609 | my $gitdirfh = File::Temp->newdir ( "ipxe-build-XXXXXX", DIR => $tmpdir, 610 | CLEANUP => ! $keep ); 611 | my $gitdir = $gitdirfh->dirname; 612 | warn "Temporary git directory: ".$gitdir."\n" if $verbosity > 0; 613 | my $worktreefh = File::Temp->newdir ( "ipxe-build-XXXXXX", DIR => $tmpdir, 614 | CLEANUP => ! $keep ); 615 | my $worktree = $worktreefh->dirname; 616 | warn "Temporary working tree: ".$worktree."\n" if $verbosity > 0; 617 | 618 | # Clone git tree into temporary directory 619 | warn "Cloning git tree from ".$repository_git."...\n" if $verbosity > 1; 620 | systemx ( "git", "clone", "--quiet", "--local", "--shared", "--bare", 621 | $repository_git, $gitdir ); 622 | 623 | # Find best cached binary set, if any 624 | my $cached = load_cached_binaries ( $gitdir, $worktree, $revision, $bindir ); 625 | 626 | # Check out git revision into temporary directory 627 | warn "Checking out revision ".$revision."...\n" if $verbosity > 1; 628 | systemx ( "git", "--git-dir", $gitdir, "--work-tree", $worktree, 629 | "checkout", "--quiet", $revision ); 630 | 631 | # Create cached binary set, if necessary 632 | save_cached_binaries ( $gitdir, $worktree, $revision, $bindir ) 633 | unless ( defined $cached ) && ( $cached eq $revision ); 634 | 635 | # Generate config/local/*.h 636 | config_local ( $worktree, $params ); 637 | 638 | # Build final target 639 | my $target = catfile ( $bindir, $binary ); 640 | warn "Building final target ".$target."...\n" if $verbosity > 1; 641 | systemx ( "make", "-C", catdir ( $worktree, "src" ), 642 | ( $concurrency ? ( "-j", $concurrency ) : () ), 643 | ( $debug ? ( "DEBUG=".$debug ) : () ), 644 | ( $embed ? ( "EMBEDDED_IMAGE=".$embed ) : () ), 645 | $target ); 646 | 647 | # Return target 648 | my $outfile = catfile ( $worktree, "src", $bindir, $binary ); 649 | warn "Returning final target ".$outfile."...\n" if $verbosity > 1; 650 | open my $outfh, "<", $outfile 651 | or die "Could not open ".$outfile.": $!\n"; 652 | return ( $outfh, $binary ); 653 | } 654 | 655 | ############################################################################### 656 | # 657 | # Copy data 658 | # 659 | # File::Copy seems to directly use the underlying filehandles, which 660 | # doesn't play nicely with the I/O layer mangling used by FCGI. 661 | # 662 | 663 | use constant COPY_BLKSIZE => 4096; 664 | 665 | sub copy { 666 | my $infh = shift; 667 | my $outfh = shift; 668 | 669 | while ( 1 ) { 670 | my $len = read ( $infh, my $buffer, COPY_BLKSIZE ); 671 | die "Could not copy data: $!\n" if ! defined $len; 672 | last unless $len; 673 | print $outfh $buffer; 674 | } 675 | } 676 | 677 | ############################################################################### 678 | # 679 | # Main loop 680 | # 681 | 682 | # Avoid zombies 683 | $SIG{CHLD} = "IGNORE"; 684 | 685 | # Hack around FCGI's apparent inability to cope gracefully with child workers 686 | my $fcgi_destroy_override = Sub::Override->new ( "FCGI::DESTROY", sub {} ); 687 | 688 | while ( 1 ) { 689 | my $fcgiin = IO::Handle->new(); 690 | my $fcgiout = IO::Handle->new(); 691 | my $request = FCGI::Request ( $fcgiin, $fcgiout, $fcgiout ); 692 | last if $request->Accept() < 0; 693 | 694 | # Connect up dummy FCGI handle if running from command line 695 | if ( ! $request->IsFastCGI() ) { 696 | open $fcgiin, "<&", \*STDIN or die "Could not dup STDIN: $!\n"; 697 | open $fcgiout, ">&", \*STDOUT or die "Could not dup STDOUT: $!\n"; 698 | } 699 | 700 | # Fork child process 701 | $request->Detach(); 702 | if ( ! $foreground ) { 703 | my $child = fork(); 704 | if ( ! defined $child ) { 705 | die "Could not fork: $!\n"; 706 | } elsif ( $child ) { 707 | next; 708 | } 709 | } 710 | $fcgi_destroy_override->restore(); 711 | $request->Attach(); 712 | $request->LastCall(); 713 | 714 | # Create CGI object 715 | my $cgi; 716 | { 717 | # CGI hardcodes the use of STDIN, relying upon the I/O layer 718 | # mangling used by FCGI to change the definition of STDIN. Since 719 | # we need to be able to close and reopen the original STDIN, we 720 | # tell FCGI to leave STDIN alone and use $fcgiin instead. We must 721 | # therefore temporarily redefine STDIN to keep CGI happy. 722 | local *STDIN = $fcgiin; 723 | $cgi = CGI->new(); 724 | } 725 | 726 | # Allow system() et al to work normally 727 | undef $SIG{CHLD}; 728 | 729 | # Set niceness 730 | nice ( $niceness ); 731 | 732 | # Redirect STDOUT and STDERR to log file if applicable 733 | my $logfh = ( -t STDERR ? undef : File::Temp->new() ); 734 | if ( $logfh ) { 735 | $logfh->autoflush(); 736 | close STDOUT; 737 | open STDOUT, ">&", $logfh or die "Could not dup logfh for STDOUT: $!\n"; 738 | close STDERR; 739 | open STDERR, ">&", $logfh or die "Could not dup logfh for STDERR: $!\n"; 740 | } 741 | 742 | # Perform build 743 | ( my $outfh, my $binary ) = eval { build ( $cgi ) }; 744 | my $build_error = $@; 745 | warn $build_error if $build_error; 746 | 747 | # Restore STDOUT and STDERR 748 | close STDOUT; 749 | open STDOUT, ">&", $origstdout or die "Could not restore STDOUT: $!\n"; 750 | close STDERR; 751 | open STDERR, ">&", $origstderr or die "Could not restore STDERR: $!\n"; 752 | 753 | # Send output to client 754 | if ( $outfh ) { 755 | my $stat = stat ( $outfh ) or die "Could not stat ".$binary.": $!\n"; 756 | print $fcgiout $cgi->header ( -status => "200 OK", 757 | -type => "application/octet-stream", 758 | -attachment => $binary, 759 | -Content_Length => $stat->size, 760 | -Pragma => "no-cache", 761 | -Cache_Control => "no-cache" ); 762 | if ( -t $fcgiout ) { 763 | warn "\n"; 764 | } else { 765 | copy ( $outfh, $fcgiout ); 766 | } 767 | } else { 768 | print $fcgiout $cgi->header ( -status => "500 Internal server error", 769 | -type => "text/plain" ); 770 | print $fcgiout "Build failed:\n\n".$build_error."\n\n"; 771 | if ( $logfh ) { 772 | print $fcgiout "Build log:\n"; 773 | $logfh->seek ( 0, SEEK_SET ); 774 | copy ( $logfh, $fcgiout ); 775 | } 776 | } 777 | 778 | # Exit child 779 | exit ( 0 ); 780 | } 781 | 782 | -------------------------------------------------------------------------------- /html/build.ini: -------------------------------------------------------------------------------- 1 | [source] 2 | repository=/tmp/ipxe/source 3 | 4 | [build] 5 | niceness=10 6 | concurrency=16 7 | bindirs=bin bin-i386-efi bin-x86_64-efi bin-i386-linux bin-x86_64-efi 8 | tmpdir=/tmp/ipxe/build 9 | 10 | [cache] 11 | root=/tmp/ipxe/cache 12 | lockfile=/tmp/ipxe/cache/.cache.lock 13 | max=20 14 | 15 | -------------------------------------------------------------------------------- /html/css/ui.css: -------------------------------------------------------------------------------- 1 | /* 2 | * ================================================================================ 3 | * Dynamic iPXE image generator 4 | * 5 | * Copyright (C) 2020 James DeVincentis. Based on work of Francois Lacroix. 6 | * Copyright (C) 2012-2019 Francois Lacroix. All Rights Reserved. 7 | * Website: http://ipxe.org, https://github.com/xbgmsharp/ipxe-buildweb 8 | * https://github.com/lps-rocks/rom-o-matic 9 | * License: GNU General Public License version 3 or later; see LICENSE.txt 10 | * ================================================================================ 11 | */ 12 | 13 | body { 14 | background-color: #eee; 15 | font-family: "Arial", "Helvetica", "Verdana", "sans-serif"; 16 | font-size: 12px; 17 | margin: 0; 18 | padding: 0; 19 | } 20 | 21 | h1 { 22 | background-color: #333; 23 | color: #fff; 24 | font-size: 2.0em; 25 | margin: 0; 26 | padding: 10px; 27 | } 28 | 29 | h1 a { 30 | color: #fff; 31 | text-decoration: underline; 32 | } 33 | 34 | h1 a:hover { 35 | color: #eee; 36 | text-decoration: underline; 37 | } 38 | 39 | h3 span { 40 | font-weight: normal; 41 | } 42 | 43 | #about_pop_up { 44 | background-color:#fff; 45 | border-radius:15px; 46 | color:#000; 47 | display:none; 48 | padding:20px; 49 | min-width:400px; 50 | min-height: 180px; 51 | } 52 | 53 | .b-close{ 54 | cursor:pointer; 55 | position:absolute; 56 | right:10px; 57 | top:5px; 58 | } 59 | 60 | #wrapper { 61 | float: left; 62 | width: 50%; 63 | background-color: #fff; 64 | border: #ddd 1px solid; 65 | border-radius: 10px; 66 | margin: 10px; 67 | padding: 10px; 68 | } 69 | 70 | #notes { 71 | float: right; 72 | width: 40%; 73 | background-color: #ffe; 74 | border: #ddd 1px solid; 75 | border-radius: 10px; 76 | margin: 10px; 77 | padding: 10px; 78 | } 79 | 80 | .spacer { 81 | margin-top: 12px; 82 | } 83 | 84 | #drop_zone { 85 | border: 2px dashed rgb(187, 187, 187); 86 | border-radius: 5px 5px 5px 5px; 87 | padding: 25px; 88 | text-align: center; 89 | color: rgb(187, 187, 187); 90 | } 91 | 92 | #W3valid { 93 | position: fixed; 94 | right: 0px; 95 | bottom: 0px; 96 | } 97 | 98 | .help { 99 | cursor: help; 100 | color: #666; 101 | text-decoration: none; 102 | } 103 | 104 | .help:hover{ 105 | cursor: help; 106 | color: #000; 107 | text-decoration: underline; 108 | } 109 | 110 | .help_buildcfg { 111 | cursor: help; 112 | color: #000; 113 | text-decoration: none; 114 | } 115 | 116 | .help_buildcfg:hover{ 117 | cursor: help; 118 | color: #000; 119 | text-decoration: underline; 120 | } 121 | 122 | .error { 123 | background-color: #FF0000; 124 | text-decoration: underline; 125 | font-size: 14px; 126 | } 127 | 128 | .wizard-header { 129 | background-color: #f4f4f4; 130 | border-bottom: #ddd 1px solid; 131 | border-top-left-radius: 10px; 132 | border-top-right-radius: 10px; 133 | padding: 5px 10px; 134 | margin: 0 0 10px 0; 135 | } 136 | 137 | .wizard-option { 138 | background-color: #f1f1f1; 139 | border-bottom: #ddd 1px solid; 140 | border-top-left-radius: 10px; 141 | border-top-right-radius: 10px; 142 | padding: 5px 20px; 143 | margin: 0 0 5px 0; 144 | } 145 | 146 | .noscript { 147 | font-size: 25px; 148 | background-color: red; 149 | text-align: center; 150 | position: fixed; 151 | left: 0px; 152 | bottom: 50%; 153 | } 154 | 155 | #result,#build,#gitversion,#debug,#embedded { 156 | display: none; 157 | } 158 | 159 | #options { 160 | box-shadow: 0px 0px 3px #aaa; 161 | border-radius: 10px; 162 | border: 2px dashed rgb(187, 187, 187); 163 | border-radius: 5px 5px 5px 5px; 164 | } 165 | 166 | #options label { 167 | padding-left: 20px; 168 | } 169 | 170 | .buildbutton { 171 | border: none; 172 | outline: none; 173 | border-radius: 10px; 174 | color: #ffffff; 175 | display: inline; 176 | cursor:pointer; 177 | margin: 0px auto; 178 | clear: both; 179 | padding: 7px 25px; 180 | text-shadow: 0 1px 1px #777; 181 | font-weight: bold; 182 | font-family:"Century Gothic", Helvetica, sans-serif; 183 | font-size: 22px; 184 | box-shadow: 0px 0px 3px #aaa; 185 | background: #4797ED; 186 | } 187 | 188 | .buildbutton:hover { 189 | background: #d8d8d8; 190 | color: #666; 191 | text-shadow: 1px 1px 1px #fff; 192 | } 193 | 194 | .savebutton { 195 | border: none; 196 | outline: none; 197 | border-radius: 10px; 198 | color: #ffffff; 199 | display: inline; 200 | cursor:pointer; 201 | margin: 0px auto; 202 | clear: both; 203 | padding: 7px 25px; 204 | text-shadow: 0 1px 1px #777; 205 | font-weight: bold; 206 | font-family:"Century Gothic", Helvetica, sans-serif; 207 | font-size: 22px; 208 | box-shadow: 0px 0px 3px #aaa; 209 | background: #16A70C; 210 | } 211 | 212 | .savebutton:hover { 213 | background: #d8d8d8; 214 | color: #666; 215 | text-shadow: 1px 1px 1px #fff; 216 | } 217 | 218 | -------------------------------------------------------------------------------- /html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lps-rocks/rom-o-matic/1a387e31f30e0ff7d9bdf50c77863e0fdaf7ef67/html/favicon.ico -------------------------------------------------------------------------------- /html/images/forkme_right_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lps-rocks/rom-o-matic/1a387e31f30e0ff7d9bdf50c77863e0fdaf7ef67/html/images/forkme_right_red.png -------------------------------------------------------------------------------- /html/images/html5-badge-h-css3-semantics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lps-rocks/rom-o-matic/1a387e31f30e0ff7d9bdf50c77863e0fdaf7ef67/html/images/html5-badge-h-css3-semantics.png -------------------------------------------------------------------------------- /html/images/valid-html5-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lps-rocks/rom-o-matic/1a387e31f30e0ff7d9bdf50c77863e0fdaf7ef67/html/images/valid-html5-button.png -------------------------------------------------------------------------------- /html/images/vcss-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lps-rocks/rom-o-matic/1a387e31f30e0ff7d9bdf50c77863e0fdaf7ef67/html/images/vcss-blue.png -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | ROM-o-matic | Generate iPXE images | open source network boot firmware 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |

27 | Generate iPXE images | About 28 |

29 | 30 |
31 |
32 | 33 |
34 |
35 | 36 |
37 |

38 | This wizard will guide you through the customized iPXE image generation.
39 |

40 | 41 | 42 |
43 | 44 | 45 |
46 | 47 |
48 |
49 |
50 |

Choose an output format:

51 |

52 | Help me choose 53 |

54 | 61 |
62 |
63 |
64 | 65 | 133 | 134 | 147 | 148 | 155 | 156 | 165 | 166 | 175 | 176 | 182 |
183 |
184 | 185 |
186 |
187 |

Information:

188 | 192 |
193 |
194 |

Notes:

195 | For help and information about getting your OS loaded with iPXE or Network boot:
196 | 204 |
205 |
206 | 207 |
208 | HTML5 Valid 209 | CSS3 Valid 210 | HTML5 Powered with CSS3 / Styling, and Semantics 211 |
212 | 213 | Fork me on GitHub 214 | 215 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /html/js/jquery.bpopup.min.js: -------------------------------------------------------------------------------- 1 | /*================================================================================ 2 | * @name: bPopup - if you can't get it up, use bPopup 3 | * @author: (c)Bjoern Klinggaard (twitter@bklinggaard) 4 | * @demo: http://dinbror.dk/bpopup 5 | * @version: 0.9.3.min 6 | ================================================================================*/ 7 | (function(b){b.fn.bPopup=function(u,C){function v(){a.modal&&b('
').css({backgroundColor:a.modalColor,position:"fixed",top:0,right:0,bottom:0,left:0,opacity:0,zIndex:a.zIndex+l}).appendTo(a.appendTo).fadeTo(a.speed,a.opacity);z();c.data("bPopup",a).data("id",e).css({left:"slideIn"===a.transition?-1*(m+h):n(!(!a.follow[0]&&p||g)),position:a.positionStyle||"absolute",top:"slideDown"===a.transition?-1*(q+h):r(!(!a.follow[1]&&s||g)),"z-index":a.zIndex+l+1}).each(function(){a.appending&&b(this).appendTo(a.appendTo)});D(!0)}function t(){a.modal&&b(".b-modal."+c.data("id")).fadeTo(a.speed,0,function(){b(this).remove()});a.scrollBar||b("html").css("overflow","auto");b(".b-modal."+e).unbind("click");j.unbind("keydown."+e);d.unbind("."+e).data("bPopup",0=c.height()&&(d.height=c.height());b>=c.width()&&(d.width=c.width());w=c.outerHeight(!0);h=c.outerWidth(!0);z();a.contentContainer.css({height:"auto",width:"auto"});d.left=n(!(!a.follow[0]&&p||g));d.top=r(!(!a.follow[1]&&s||g));c.animate(d,250,function(){f.show();x=A()})}function D(f){switch(a.transition){case "slideIn":c.css({display:"block",opacity:1}).animate({left:f?n(!(!a.follow[0]&&p||g)):j.scrollLeft()-(h||c.outerWidth(!0))-200},a.speed,a.easing,function(){B(f)});break;case "slideDown":c.css({display:"block",opacity:1}).animate({top:f?r(!(!a.follow[1]&&s||g)):j.scrollTop()-(w||c.outerHeight(!0))-200},a.speed,a.easing,function(){B(f)});break;default:c.stop().fadeTo(a.speed,f?1:0,function(){B(f)})}}function B(f){f?(d.data("bPopup",l),c.delegate(".bClose, ."+a.closeClass,"click."+e,t),a.modalClose&&b(".b-modal."+e).css("cursor","pointer").bind("click",t),!G&&(a.follow[0]||a.follow[1])&&d.bind("scroll."+e,function(){x&&c.dequeue().animate({left:a.follow[0]?n(!g):"auto",top:a.follow[1]?r(!g):"auto"},a.followSpeed,a.followEasing)}).bind("resize."+e,function(){if(x=A())clearTimeout(F),F=setTimeout(function(){z();c.dequeue().each(function(){g?b(this).css({left:m,top:q}):b(this).animate({left:a.follow[0]?n(!0):"auto",top:a.follow[1]?r(!0):"auto"},a.followSpeed,a.followEasing)})},50)}),a.escClose&&j.bind("keydown."+e,function(a){27==a.which&&t()}),k(C)):(c.hide(),k(a.onClose),a.loadUrl&&(a.contentContainer.empty(),c.css({height:"auto",width:"auto"})))}function n(a){return a?m+j.scrollLeft():m}function r(a){return a?q+j.scrollTop():q}function k(a){b.isFunction(a)&&a.call(c)}function z(){var b;s?b=a.position[1]:(b=((window.innerHeight||d.height())-c.outerHeight(!0))/2-a.amsl,b=bc.outerHeight(!0)+y&&(window.innerWidth||d.width())>c.outerWidth(!0)+y}b.isFunction(u)&&(C=u,u=null);var a=b.extend({},b.fn.bPopup.defaults,u);a.scrollBar||b("html").css("overflow","hidden");var c=this,j=b(document),d=b(window),G=/OS 6(_\d)+/i.test(navigator.userAgent),y=20,l=0,e,x,s,p,g,q,m,w,h,F;c.close=function(){a=this.data("bPopup");e="__b-popup"+d.data("bPopup")+"__";t()};return c.each(function(){if(!b(this).data("bPopup"))if(k(a.onOpen),l=(d.data("bPopup")||0)+1,e="__b-popup"+l+"__",s="auto"!==a.position[1],p="auto"!==a.position[0],g="fixed"===a.positionStyle,w=c.outerHeight(!0),h=c.outerWidth(!0),a.loadUrl)switch(a.contentContainer=b(a.contentContainer||c),a.content){case "iframe":var f=b('');f.appendTo(a.contentContainer);w=c.outerHeight(!0);h=c.outerWidth(!0);v();f.attr("src",a.loadUrl);k(a.loadCallback);break;case "image":v();b("").load(function(){k(a.loadCallback);E(b(this))}).attr("src",a.loadUrl).hide().appendTo(a.contentContainer);break;default:v(),b('
').load(a.loadUrl,a.loadData,function(){k(a.loadCallback);E(b(this))}).hide().appendTo(a.contentContainer)}else v()})};b.fn.bPopup.defaults={amsl:50,appending:!0,appendTo:"body",closeClass:"b-close",content:"ajax",contentContainer:!1,easing:"swing",escClose:!0,follow:[!0,!0],followEasing:"swing",followSpeed:500,loadCallback:!1,loadData:!1,loadUrl:!1,modal:!0,modalClose:!0,modalColor:"#000",onClose:!1,onOpen:!1,opacity:0.7,position:["auto","auto"],positionStyle:"absolute",scrollBar:!0,speed:250,transition:"fadeIn",zIndex:9997}})(jQuery); 8 | -------------------------------------------------------------------------------- /html/js/ui.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ================================================================================ 3 | * Dynamic iPXE image generator 4 | * 5 | * Copyright (C) 2020 James DeVincentis. Based on work of Francois Lacroix. 6 | * Copyright (C) 2012-2019 Francois Lacroix. All Rights Reserved. 7 | * Website: http://ipxe.org, https://github.com/xbgmsharp/ipxe-buildweb 8 | * https://github.com/lps-rocks/rom-o-matic 9 | * License: GNU General Public License version 3 or later; see LICENSE.txt 10 | * ================================================================================ 11 | */ 12 | $(document).ready(function() { 13 | 14 | var roms = []; /* Global Object for roms ID validation */ 15 | 16 | $.getJSON("versions.json", null, function(data) { 17 | //alert(data[0]); 18 | var git = '

Generating iPXE build image version ' + data[0] + '

'; 19 | $("#gitabbrev").html(git); 20 | var options = ""; 21 | for (var i = 0; i < data.length; i++) { 22 | //alert(data[i]); 23 | options += ''; 24 | } 25 | $("#gitrevision").html(options); 26 | }) 27 | 28 | $.getJSON("nics.json", null, function(listnics) { 29 | //alert(listnics.length); 30 | var options = '\n\n'; 31 | for (var i = 0; i < listnics.length; i++) { 32 | //alert(listnics[i].device_name); 33 | //alert(listnics[i].ipxe_name); 34 | options += ''; 35 | if (listnics[i].device_id != null && listnics[i].vendor_id != null) { 36 | roms.push({device_id: listnics[i].device_id, vendor_id: listnics[i].vendor_id}); 37 | } 38 | } 39 | $("#nics").html(options); 40 | }) 41 | 42 | $.getJSON("options.json", null, function(custom) { 43 | //alert(custom.length); 44 | 45 | // List of subtitle of options 46 | var subtitle = new Object; 47 | subtitle._CMD = 'Command-line commands to include:'; 48 | subtitle.NET_PROTO = 'Network protocols:'; 49 | subtitle.IMAGE = 'Image types:'; 50 | subtitle.PXE_ = 'PXE support:'; 51 | subtitle.COM = 'Serial options:'; 52 | subtitle.DOWNLOAD_PROTO = 'Download protocols:'; 53 | subtitle.SANBOOT_PROTO = 'SAN boot protocols:'; 54 | subtitle.CRYPTO_80211 = 'Wireless Interface Options:'; 55 | subtitle.CONSOLE = 'Console options:'; 56 | subtitle.ISA = 'ISA options:'; 57 | subtitle.PCIAPI = 'PCIAPI options:'; 58 | subtitle.COLOR = 'Color options:'; 59 | subtitle.DNS = 'Name resolution modules:'; 60 | subtitle.VMWARE = 'VMware options:' 61 | subtitle.GDB = 'Debugger options:' 62 | subtitle.NONPNP = 'ROM-specific options:'; 63 | subtitle.ERRMSG = 'Error message tables to include:'; 64 | subtitle.BANNER = 'Timer configuration:'; 65 | subtitle.NETDEV = 'Obscure configuration options:'; 66 | subtitle.PRODUCT = 'Branding options:'; 67 | subtitle.DHCP = 'DHCP timeout parameters:'; 68 | subtitle.PXEBS = 'PXE Boot Server timeout parameters:'; 69 | subtitle.USB = 'USB configuration:'; 70 | subtitle.HTTP = 'HTTP extensions:'; 71 | subtitle.VNIC = 'Virtual network devices:'; 72 | 73 | var listoptions = ''; 74 | var previous; 75 | var desc; 76 | for (var i = 0; i < custom.length; i++) { 77 | //alert(custom[i].name); 78 | //alert(custom[i].description); 79 | for (var y in subtitle) 80 | { 81 | var regexp = new RegExp(y); 82 | var match = regexp.exec(custom[i].name); 83 | //if (custom[i].name.indexOf("_CMD", custom[i].name.length - 4) !== -1) 84 | /*if (custom[i].name === "PXE_CMD") 85 | { 86 | console.log('['+ y + '] vs [' + custom[i].name + '] match:' + match + ' && previous:' + previous); 87 | }*/ 88 | if (previous == y && match == y) 89 | { 90 | break; 91 | } 92 | else if (match != null && previous != y) 93 | { 94 | listoptions += '

'+ subtitle[y] + '

' 95 | previous = y; 96 | break; 97 | } 98 | } 99 | if (custom[i].type == "define" && (custom[i].name.indexOf("BANNER") !== -1)) { 100 | desc = custom[i].description; 101 | if (custom[i].name === custom[i].description) { desc = ""; } 102 | listoptions += '

'; 103 | } else if (custom[i].type == "define" && (custom[i].description.indexOf("0x") !== -1)) { 104 | desc = custom[i].description; 105 | if (custom[i].name === custom[i].description) { desc = ""; } 106 | listoptions += '

'; 107 | } else if (custom[i].type == "define") { 108 | custom[i].href_help_name = '' + custom[i].name + ''; 109 | listoptions += '

'; 110 | } else if (custom[i].type == "undef") { 111 | custom[i].href_help_name = '' + custom[i].name + ''; 112 | listoptions += '

'; 113 | } else if (custom[i].type == "input") { 114 | desc = custom[i].description; 115 | if (custom[i].name === custom[i].description) { desc = ""; } 116 | if (custom[i].name.indexOf("PRODUCT") !== -1) 117 | listoptions += '

'; 118 | else 119 | listoptions += '

'; 120 | } else { alert("we have an issue"); } 121 | } 122 | $("#options").html(listoptions); 123 | }) 124 | 125 | /* Reset from on reload */ 126 | $("input[name=wizardtype]:first").prop('checked', true); 127 | $("#outputformatstd").prop('selectedIndex', 0); 128 | $("#outputformatadv").prop('selectedIndex', 0); 129 | 130 | $("#formtype").change(function(){ 131 | var wizardtype = $('input:radio[name=wizardtype]:checked').val(); 132 | //alert(wizardtype); 133 | if (wizardtype == "standard") 134 | { 135 | $("#divstandard").css({'display': 'inline'}); 136 | $("#divadvanced").css({'display': 'none'}); 137 | } 138 | else if (wizardtype == "advanced") 139 | { 140 | $("#divstandard").css({'display': 'none'}); 141 | $("#divadvanced").css({'display': 'inline'}); 142 | } 143 | }); 144 | 145 | $("#gitrevision").change(function(){ 146 | var gitversion = $("#gitrevision").val(); 147 | var git = '

Generating iPXE build image version ' + gitversion + '

'; 148 | $("#gitabbrev").html(git); 149 | }); 150 | 151 | $("#outputformatstd").change(function(){ 152 | var outputformat = $("#outputformatstd").val(); 153 | //alert(outputformat); 154 | if (outputformat == "-") 155 | { 156 | $("#embedded").css({'display': 'none'}); 157 | $("#debug").css({'display': 'none'}); 158 | $("#gitversion").css({'display': 'none'}); 159 | $("#build").css({'display': 'none'}); 160 | } 161 | else 162 | { 163 | $("#embedded").css({'display': 'inline'}); 164 | $("#debug").css({'display': 'inline'}); 165 | $("#gitversion").css({'display': 'inline'}); 166 | $("#build").css({'display': 'inline'}); 167 | } 168 | }); 169 | 170 | $("#outputformatadv").change(function(){ 171 | var outputformat = $("#outputformatadv").val(); 172 | //alert(outputformat); 173 | if (outputformat.indexOf("rom", outputformat.length - 3) !== -1) 174 | { /* If a ROM */ 175 | $("#rom").css({'display': 'inline'}); 176 | $("#iface").css({'display': 'none'}); 177 | $("#config").css({'display': 'none'}); 178 | $("#embedded").css({'display': 'inline'}); 179 | $("#debug").css({'display': 'none'}); 180 | $("#gitversion").css({'display': 'inline'}); 181 | $("#build").css({'display': 'inline'}); 182 | } 183 | else if (outputformat == "-" || outputformat == "--") 184 | { /* If default */ 185 | $("#rom").css({'display': 'none'}); 186 | $("#iface").css({'display': 'none'}); 187 | $("#config").css({'display': 'none'}); 188 | $("#embedded").css({'display': 'none'}); 189 | $("#debug").css({'display': 'none'}); 190 | $("#gitversion").css({'display': 'none'}); 191 | $("#build").css({'display': 'none'}); 192 | } 193 | else 194 | { 195 | $("#rom").css({'display': 'none'}); 196 | $("#iface").css({'display': 'inline'}); 197 | $("#config").css({'display': 'inline'}); 198 | $("#embedded").css({'display': 'inline'}); 199 | $("#debug").css({'display': 'inline'}); 200 | $("#gitversion").css({'display': 'inline'}); 201 | $("#build").css({'display': 'inline'}); 202 | } 203 | }); 204 | 205 | /* Compose build.fcgi url */ 206 | function buildcfg() { 207 | /* Get values from form */ 208 | var wizard = $('input:radio[name=wizardtype]:checked').val(); 209 | var bindir = ""; 210 | var binary = ""; 211 | var options = ""; 212 | /* Get generic values from form */ 213 | var debug = escape($("#setdebug").val()); 214 | var revision = $("#gitrevision").val(); 215 | var embed = escape($("#embed").val()); 216 | if (embed == "#!ipxe") { embed = ""; } 217 | if (wizard == "standard") 218 | { /* get values from elements on the STD wizard */ 219 | bindir = $("#outputformatstd").val().split("/")[0]; 220 | binary = $("#outputformatstd").val().split("/")[1]; 221 | } 222 | else if (wizard == "advanced") 223 | { /* get values from elements on the ADV wizard */ 224 | bindir = $("#outputformatadv").val().split("/")[0]; 225 | binary = $("#outputformatadv").val().split("/")[1]; 226 | if (binary.indexOf("rom", binary.length - 3) !== -1) 227 | { 228 | /* Ensure device_id and vendor_id are valid */ 229 | var pci_vendor_code = $("#pci_vendor_code").val().toLowerCase(); 230 | var pci_device_code = $("#pci_device_code").val().toLowerCase(); 231 | /* using jQuery.grep as polyfill for older browsers without Array.prototype.filter */ 232 | var idx_nic = $.grep(roms, function(obj) { 233 | return obj.vendor_id == pci_vendor_code && obj.device_id == pci_device_code; 234 | }); 235 | if (pci_vendor_code && pci_device_code && idx_nic && idx_nic.length > 0) { 236 | binary = $("#pci_vendor_code").val().toLowerCase() + $("#pci_device_code").val().toLowerCase() + "." + binary; 237 | } else { 238 | $("#pci_roms_id_error").css({'display': 'inline'}); 239 | $("#pci_roms_id_error").html("Invalid or unsupported pci_vendor_code or pci_device_code
"); 240 | return; 241 | } 242 | } 243 | /* For all Checkbox in options div */ 244 | $("#options").find("input:checkbox").each(function(index) { 245 | var name = $(this).prop("name"); 246 | var value = $(this).prop("checked") ? 1 : 0; 247 | if ($(this).val() != value) { 248 | console.log( "Checkbox:" + index + ": " + name + " default: " + $(this).val() + " new: " + $(this).prop("checked") ); 249 | options += name + ":=" + value + "&"; 250 | } 251 | }); 252 | /* For all text field in options div */ 253 | $("#options").find("input:text").each(function(index) { 254 | var name = $(this).prop("name"); 255 | var placeholder = $(this).prop("placeholder"); 256 | if ($(this).val() != placeholder) { 257 | console.log( "Text:" + index + ": " + name + " default: " + $(this).prop("placeholder") + " new: " + $(this).val()); 258 | options += name + "=" + escape($(this).val()) + "&"; 259 | } 260 | }); 261 | } 262 | 263 | console.log('{ BINARY: ['+ binary +'], BINDIR: ['+ bindir +'], DEBUG: ['+ debug +'], REVISION: ['+ revision +'], EMBED: ['+ embed +'] , OPTIONS: ['+ options +']}'); 264 | 265 | return 'build.fcgi?BINARY='+binary+'&BINDIR='+bindir+'&REVISION='+revision+'&DEBUG='+debug+'&EMBED.00script.ipxe='+embed+'&'+options; 266 | }; 267 | 268 | /* Update fields with defaults from current URL */ 269 | function loadcfg(delay) { 270 | /* if the form is not ready, wait a while */ 271 | if ($("#gitrevision,#nics,#options").length != 3 272 | || $("#gitrevision").html().trim().length == 0 273 | || $("#nics").html().trim().length == 0 274 | || $("#options").html().trim().length == 0 275 | ) { 276 | if (typeof delay == "undefined") { 277 | delay = 0; 278 | } 279 | if (delay < 700) { 280 | delay = (delay*2)+100; 281 | } else if (delay < 3000) { 282 | delay += 100 283 | } else { 284 | delay = 3000 285 | } 286 | setTimeout(function(){loadcfg(delay);}, delay); 287 | return; 288 | } 289 | /* Parse querystring and generate args */ 290 | var querystring = window.location.search; 291 | querystring = querystring.replace(/^\?+/, ''); 292 | if (querystring == "") return; 293 | querystring = querystring.split('&'); 294 | var args = {}; 295 | querystring.forEach(function(item){ 296 | var key = item.split('=', 1)[0]; 297 | var value = item.substr(key.length + 1); 298 | key = key.replace(/:$/, ''); 299 | args[key] = value; 300 | }); 301 | /* Get values from args */ 302 | //$('input:radio[name=wizardtype]#advanced').prop('checked', true); 303 | $('input:radio[name=wizardtype]#advanced').click(); 304 | var bindir = (args['BINDIR'] > "") ? args['BINDIR'] : ""; 305 | var binary = (args['BINARY'] > "") ? args['BINARY'] : ""; 306 | var debug = (args['DEBUG'] > "") ? args['DEBUG'] : ""; 307 | var revision = (args['REVISION'] > "") ? args['REVISION'] : ""; 308 | var embed = (args['EMBED.00script.ipxe'] > "") ? args['EMBED.00script.ipxe'] : ""; 309 | /* parse pci id for rom images */ 310 | if (binary.indexOf("rom", binary.length - 3) !== -1) 311 | { 312 | var pci_id_code = binary.split('.', 1)[0]; 313 | binary = binary.substr(pci_id_code.length + 1); 314 | if (pci_id_code.length != 8) { 315 | console.error("Unknown format for pci id: ", pci_id_code); 316 | } else { 317 | /* Ensure device_id and vendor_id are valid */ 318 | var pci_vendor_code = pci_id_code.substr(0,4); 319 | var pci_device_code = pci_id_code.substr(4,4); 320 | $("#pci_vendor_code").val(pci_vendor_code).change(); 321 | $("#pci_device_code").val(pci_device_code).change(); 322 | /* using jQuery.grep as polyfill for older browsers without Array.prototype.filter */ 323 | var idx_nic = $.grep(roms, function(obj) { 324 | return obj.vendor_id == pci_vendor_code && obj.device_id == pci_device_code; 325 | }); 326 | if (!pci_vendor_code || !pci_device_code || !idx_nic || idx_nic.length == 0) { 327 | $("#pci_roms_id_error").css({'display': 'inline'}); 328 | $("#pci_roms_id_error").html("Invalid or unsupported pci_vendor_code or pci_device_code
"); 329 | } 330 | } 331 | } 332 | /* build up full binary string */ 333 | var fullbinary = bindir + "/" + binary; 334 | /* Set values in form */ 335 | $("#setdebug").val(unescape(debug)); 336 | $("#gitrevision").val(unescape(revision)); 337 | $("#embed").val(unescape(embed)) 338 | /* Set values for elements in the ADV wizard */ 339 | $("#outputformatadv").val(fullbinary).change(); 340 | /* For all Checkboxes in options div */ 341 | $("#options").find("input:checkbox").each(function(index) { 342 | var name = $(this).prop("name"); 343 | var value = $(this).prop("checked") ? 1 : 0; 344 | if (typeof args[name] != "undefined" && value != args[name]) { 345 | $(this).prop("checked", args[name] == 1); 346 | } 347 | }); 348 | /* For all text field in options div */ 349 | $("#options").find("input:text").each(function(index) { 350 | var name = $(this).prop("name"); 351 | if (typeof args[name] != "undefined") { 352 | $(this).val(args[name]); 353 | } 354 | }); 355 | } 356 | 357 | $("#ipxeimage").submit(function(event) { 358 | /* stop form from submitting normally */ 359 | event.preventDefault(); 360 | var url = buildcfg(); 361 | if (url) { 362 | window.location.href = url; 363 | }; 364 | }); 365 | 366 | 367 | /* About Popup */ 368 | $(function() { 369 | 370 | /* Bind a click event */ 371 | $('#about').on('click', function(e) { 372 | 373 | /* Prevents the default action to be triggered */ 374 | e.preventDefault(); 375 | 376 | /* Triggering bPopup when click event is fired */ 377 | $('#about_pop_up').bPopup({ 378 | contentContainer:'#about_pop_up', 379 | loadUrl: 'about.html' 380 | }); 381 | }); 382 | $('#about2').on('click', function(e) { 383 | 384 | /* Prevents the default action to be triggered */ 385 | e.preventDefault(); 386 | 387 | /* Triggering bPopup when click event is fired */ 388 | $('#about_pop_up').bPopup({ 389 | contentContainer:'#about_pop_up', 390 | loadUrl: 'about.html' 391 | }); 392 | }); 393 | 394 | $('#save').on('click', function(e) { 395 | 396 | /* Prevents the default action to be triggered */ 397 | e.preventDefault(); 398 | var self = $(this) //button 399 | , content = $('.content'); 400 | 401 | /* Triggering bPopup when click event is fired */ 402 | $('#about_pop_up').bPopup({ 403 | onOpen: function() { 404 | var baseURI = document.baseURI.replace(window.location.search,'').replace(/index\.html$/,''); 405 | var config = buildcfg(); 406 | var data = "

Direct buildcfg URL

Use this URL to directly retreive your binary for later use:

"; 407 | data += "
" + baseURI + config; 408 | data += "

Editable Configuration URL

Use this URL to adjust your binary's setup:

"; 409 | var editcfg = config.replace(/^[^?]*\?/,'?'); 410 | data += "
" + baseURI + editcfg; 411 | content.html(data); 412 | }, 413 | onClose: function() { 414 | content.empty(); 415 | } 416 | }); 417 | }); 418 | setTimeout(loadcfg, 50); 419 | }); 420 | 421 | /* Input file */ 422 | $(function() { 423 | function handleFileSelect(evt) { 424 | var file = evt.target.files[0]; // FileList object 425 | 426 | // Only process text or unknow file type. 427 | if (!file.type.match('text*') && file.type != "") { 428 | document.getElementById('list').innerHTML = '
    Only text file are supported
'; 429 | return; 430 | } 431 | 432 | // file is a File objects. List some properties. 433 | var output = []; 434 | output.push('
  • ', escape(file.name), ' (', file.type || 'n/a', ') - ', 435 | file.size, ' bytes, last modified: ', 436 | file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a', 437 | '
  • '); 438 | 439 | var reader = new FileReader(); 440 | // Closure to capture the file information. 441 | reader.onload = (function(theFile) { 442 | return function(e) { 443 | var content = e.target.result; 444 | $("#embed").val(content); 445 | if (content.indexOf("#!ipxe") === -1) { 446 | document.getElementById('list').innerHTML = 447 | '
      Not a iPXE script
    '; 448 | } 449 | }; 450 | })(file); 451 | 452 | // Read in the text file as Text. 453 | reader.readAsText(file); 454 | 455 | document.getElementById('list').innerHTML = '
      ' + output.join('') + '
    '; 456 | } 457 | document.getElementById('embedfile').addEventListener('change', handleFileSelect, false); 458 | }); 459 | 460 | /* Drop file zone */ 461 | $(function() { 462 | function handleFileSelect(evt) { 463 | evt.stopPropagation(); 464 | evt.preventDefault(); 465 | 466 | //Retrieve the first (and only!) File from the FileList object 467 | var file = evt.dataTransfer.files[0]; // FileList object. 468 | 469 | // Only process textfiles. 470 | if (!file.type.match('text*') && file.type != "") { 471 | document.getElementById('list').innerHTML = '
      Only text file are supported
    '; 472 | return; 473 | } 474 | 475 | // file is a File objects. List some properties. 476 | var output = []; 477 | output.push('
  • ', escape(file.name), ' (', file.type || 'n/a', ') - ', 478 | file.size, ' bytes, last modified: ', 479 | file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a', 480 | '
  • '); 481 | 482 | var reader = new FileReader(); 483 | // Closure to capture the file information. 484 | reader.onload = (function(theFile) { 485 | return function(e) { 486 | var content = e.target.result; 487 | $("#embed").val(content); 488 | if (content.indexOf("#!ipxe") === -1) { 489 | document.getElementById('list').innerHTML = 490 | '
      Not a iPXE script
    '; 491 | } 492 | }; 493 | })(file); 494 | 495 | // Read in the text file as Text. 496 | reader.readAsText(file); 497 | 498 | document.getElementById('list').innerHTML = '
      ' + output.join('') + '
    '; 499 | $("#embedfile").val(""); 500 | } 501 | 502 | function handleDragOver(evt) { 503 | evt.stopPropagation(); 504 | evt.preventDefault(); 505 | evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy. 506 | } 507 | 508 | // Setup the dnd listeners. 509 | var dropZone = document.getElementById('drop_zone'); 510 | dropZone.addEventListener('dragover', handleDragOver, false); 511 | dropZone.addEventListener('drop', handleFileSelect, false); 512 | }); 513 | 514 | // Check for the various File API support. 515 | if (!window.File && !window.FileReader) { 516 | alert('The File APIs are not fully supported by your browser.'); 517 | } 518 | 519 | }); /* End DOM ready */ 520 | --------------------------------------------------------------------------------