pax_global_header 0000666 0000000 0000000 00000000064 14572165533 0014525 g ustar 00root root 0000000 0000000 52 comment=74efb8831a6c484584a0e760abc4d566baf1104f
nobodd-0.4/ 0000775 0000000 0000000 00000000000 14572165533 0012635 5 ustar 00root root 0000000 0000000 nobodd-0.4/.github/ 0000775 0000000 0000000 00000000000 14572165533 0014175 5 ustar 00root root 0000000 0000000 nobodd-0.4/.github/workflows/ 0000775 0000000 0000000 00000000000 14572165533 0016232 5 ustar 00root root 0000000 0000000 nobodd-0.4/.github/workflows/test.yml 0000664 0000000 0000000 00000002203 14572165533 0017731 0 ustar 00root root 0000000 0000000 name: nobodd-test-suite
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
python: "3.7"
experimental: false
- os: ubuntu-20.04
python: "3.8"
experimental: false
- os: ubuntu-20.04
python: "3.9"
experimental: false
- os: ubuntu-22.04
python: "3.10"
experimental: false
- os: ubuntu-22.04
python: "3.11"
experimental: false
- os: ubuntu-22.04
python: "3.12"
experimental: false
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
steps:
- name: Install Python ${{ matrix.python }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Checkout nobodd
uses: actions/checkout@v4
- name: Install dependencies
run: |
make develop
- name: Run tests
run: |
make test
nobodd-0.4/.gitignore 0000664 0000000 0000000 00000000371 14572165533 0014626 0 ustar 00root root 0000000 0000000 # Python stuff
*.py[cdo]
# Vim stuff
*.vim
*.swp
# Translations
*.mo
# Miscellaneous
tags
# Packages
*.egg
*.egg-info
*.pyc
*.whl
dist
build
man
# Unit test / coverage reports
coverage
.cache
.coverage
.coverage.*
.tox
.pytest_cache
.env
.eggs
nobodd-0.4/.readthedocs.yaml 0000664 0000000 0000000 00000000254 14572165533 0016065 0 ustar 00root root 0000000 0000000 version: 2
formats: all
python:
install:
- method: pip
path: .
extra_requirements:
- doc
build:
os: ubuntu-22.04
tools:
python: "3.10"
nobodd-0.4/LICENSE.txt 0000664 0000000 0000000 00000104557 14572165533 0014474 0 ustar 00root root 0000000 0000000 SPDX-License-Identifier: GPL-3.0
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
nobodd-0.4/Makefile 0000664 0000000 0000000 00000007511 14572165533 0014301 0 ustar 00root root 0000000 0000000 # vim: set noet sw=4 ts=4 fileencoding=utf-8:
# External utilities
PYTHON=python3
PIP=pip
PYTEST=pytest
TWINE=twine
PYFLAGS=
MSGINIT=msginit
MSGMERGE=msgmerge
MSGFMT=msgfmt
XGETTEXT=xgettext
DEST_DIR=/
# Calculate the base names of the distribution, the location of all source,
# documentation, packaging, icon, and executable script files
NAME:=$(shell $(PYTHON) $(PYFLAGS) setup.py --name)
WHEEL_NAME:=$(subst -,_,$(NAME))
VER:=$(shell $(PYTHON) $(PYFLAGS) setup.py --version)
PY_SOURCES:=$(shell \
$(PYTHON) $(PYFLAGS) setup.py egg_info >/dev/null 2>&1 && \
cat $(WHEEL_NAME).egg-info/SOURCES.txt | grep -v "\.egg-info" | grep -v "\.mo$$")
DOC_SOURCES:=docs/conf.py \
$(wildcard docs/*.png) \
$(wildcard docs/*.svg) \
$(wildcard docs/*.dot) \
$(wildcard docs/*.mscgen) \
$(wildcard docs/*.gpi) \
$(wildcard docs/*.rst) \
$(wildcard docs/*.pdf)
SUBDIRS:=
# Calculate the name of all outputs
DIST_WHEEL=dist/$(WHEEL_NAME)-$(VER)-py3-none-any.whl
DIST_TAR=dist/$(NAME)-$(VER).tar.gz
DIST_ZIP=dist/$(NAME)-$(VER).zip
POT_FILE=po/$(NAME).pot
PO_FILES:=$(wildcard po/*.po)
MO_FILES:=$(patsubst po/%.po,po/mo/%/LC_MESSAGES/$(NAME).mo,$(PO_FILES))
MAN_PAGES=man/nobodd-tftpd.1 man/nobodd-prep.1
# Default target
all:
@echo "make install - Install on local system"
@echo "make develop - Install symlinks for development"
@echo "make pot - Update translation template and sources"
@echo "make mo - Generate translation files"
@echo "make test - Run tests"
@echo "make doc - Generate HTML and PDF documentation"
@echo "make source - Create source package"
@echo "make wheel - Generate a PyPI wheel package"
@echo "make zip - Generate a source zip package"
@echo "make tar - Generate a source tar package"
@echo "make dist - Generate all packages"
@echo "make clean - Get rid of all generated files"
@echo "make release - Create and tag a new release"
@echo "make upload - Upload the new release to repositories"
install: $(SUBDIRS)
$(PYTHON) $(PYFLAGS) setup.py install --root $(DEST_DIR)
doc: $(DOC_SOURCES)
$(MAKE) -C docs clean
$(MAKE) -C docs html
$(MAKE) -C docs epub
$(MAKE) -C docs latexpdf
$(MAKE) $(MAN_PAGES)
preview:
$(MAKE) -C docs preview
source: $(DIST_TAR) $(DIST_ZIP)
wheel: $(DIST_WHEEL)
zip: $(DIST_ZIP)
tar: $(DIST_TAR)
dist: $(DIST_WHEEL) $(DIST_TAR) $(DIST_ZIP)
pot: $(POT_FILE) $(PO_FILES)
mo: $(MO_FILES)
develop:
@# These have to be done separately to avoid a cockup...
$(PIP) install -U setuptools
$(PIP) install -U pip
$(PIP) install -U twine
$(PIP) install -U tox
$(PIP) install -e .[doc,test]
test:
$(PYTEST)
clean:
rm -fr dist/ build/ man/ .pytest_cache/ .mypy_cache/ $(WHEEL_NAME).egg-info/ tags .coverage
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir clean; \
done
find $(CURDIR) -name "*.pyc" -delete
find $(CURDIR) -name "__pycache__" -delete
tags: $(PY_SOURCES)
ctags -R --exclude="build/*" --exclude="docs/*" --languages="Python"
lint: $(PY_SOURCES)
pylint $(WHEEL_NAME)
$(SUBDIRS):
$(MAKE) -C $@
$(MAN_PAGES): $(DOC_SOURCES)
$(MAKE) -C docs man
mkdir -p man/
cp build/man/*.[0-9] man/
$(POT_FILE): $(PY_SOURCES)
$(XGETTEXT) -o $@ $(filter %.py,$^) $(filter %.ui,$^)
po/%.po: $(POT_FILE)
$(MSGMERGE) -U $@ $<
po/mo/%/LC_MESSAGES/$(NAME).mo: po/%.po
mkdir -p $(dir $@)
$(MSGFMT) $< -o $@
$(DIST_TAR): $(PY_SOURCES) $(SUBDIRS)
$(PYTHON) $(PYFLAGS) setup.py sdist --formats gztar
$(DIST_ZIP): $(PY_SOURCES) $(SUBDIRS)
$(PYTHON) $(PYFLAGS) setup.py sdist --formats zip
$(DIST_WHEEL): $(PY_SOURCES) $(SUBDIRS)
$(PYTHON) $(PYFLAGS) setup.py bdist_wheel
release:
$(MAKE) clean
test -z "$(shell git status --porcelain)"
git tag -s v$(VER) -m "Release $(VER)"
git push origin v$(VER)
upload: $(DIST_TAR) $(DIST_WHEEL)
$(TWINE) check $(DIST_TAR) $(DIST_WHEEL)
$(TWINE) upload $(DIST_TAR) $(DIST_WHEEL)
.PHONY: all install develop test doc source wheel zip tar dist clean tags release upload $(SUBDIRS)
nobodd-0.4/README.rst 0000664 0000000 0000000 00000003475 14572165533 0014335 0 ustar 00root root 0000000 0000000 ======
nobodd
======
nobodd is a confusingly named, but simple TFTP server intended for net-booting
Raspberry Pis directly from OS images without having to loop-back mount those
images. Even customization of an image for booting on a particular board is
handled without loop devices or mounts (making it possible to operate
completely unprivileged), via a read/write FAT implementation within the
``nobodd-prep`` tool.
Usage
=====
If you have an appropriately customized OS image already placed in a file
(``ubuntu.img``), and the serial number of the Pi in question (``1234ABCD``)
then serving it as simple as:
.. code-block:: console
$ sudo nobodd-tftpd --board 1234ABCD,ubuntu.img
This defaults to reading the first partition from the file, and pretends (to
TFTP clients) that the contents of the first partition appears under the
``1234ABCD/`` directory. Hence a TFTP request for ``1234ABCD/cmdline.txt`` will
serve the ``cmdline.txt`` file from the first partition contained in
``ubuntu.img``.
The service either needs to run from root (because the default TFTP port is the
privileged port 69), or can be run as a **systemd** or **inetd**
socket-activated service, in which case the service manager will provide the
initial socket and the service can run without any special privileges.
The mapping of Pi serial numbers to OS image files can also be placed in a
configuration file under ``/etc/nobodd/conf.d``. A tool, ``nobodd-prep``, is
provided to both customize images for boot and generate basic configuration
files for ``nobodd-tftpd`` and ``nbd-server``.
Useful Links
============
* `Source code`_ on GitHub
* `Issues`_ on GitHub
* `Documentation`_ on ReadTheDocs
.. _Source code: https://github.com/waveform80/nobodd
.. _Issues: https://github.com/waveform80/nobodd/issues
.. _Documentation: https://nobodd.readthedocs.io/
nobodd-0.4/docs/ 0000775 0000000 0000000 00000000000 14572165533 0013565 5 ustar 00root root 0000000 0000000 nobodd-0.4/docs/Makefile 0000664 0000000 0000000 00000012643 14572165533 0015233 0 ustar 00root root 0000000 0000000 # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = ../build
PY_SOURCES := $(wildcard ../nobodd/*.py)
DOT_DIAGRAMS := $(wildcard images/*.dot images/*/*.dot)
MSC_DIAGRAMS := $(wildcard images/*.mscgen images/*/*.mscgen)
GPI_DIAGRAMS := $(wildcard images/*.gpi images/*/*.gpi)
SVG_IMAGES := $(wildcard images/*.svg images/*/*.svg) $(DOT_DIAGRAMS:%.dot=%.svg) $(MSC_DIAGRAMS:%.mscgen=%.svg)
PNG_IMAGES := $(wildcard images/*.png images/*/*.png) $(GPI_DIAGRAMS:%.gpi=%.png) $(SVG_IMAGES:%.svg=%.png)
PDF_IMAGES := $(SVG_IMAGES:%.svg=%.pdf) $(GPI_DIAGRAMS:%.gpi=%.pdf) $(DOT_DIAGRAMS:%.dot=%.pdf) $(MSC_DIAGRAMS:%.mscgen=%.pdf)
INKSCAPE_VER := $(shell inkscape --version | sed -ne '/^Inkscape/ s/^Inkscape \([0-9]\+\)\..*$$/\1/p')
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " preview to start a web-server that watches for file changes"
@echo " and re-builds the docs when required"
@echo " json to make JSON files"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html: $(SVG_IMAGES) $(PNG_IMAGES)
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex: $(PDF_IMAGES)
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf: $(PDF_IMAGES)
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
preview:
../scripts/previewer $(BUILDDIR)/html
%.svg: %.mscgen
mscgen -T svg -o $@ $<
%.svg: %.dot
dot -T svg -o $@ $<
%.png: %.gpi
gnuplot -e "set term pngcairo transparent size 400,400" $< > $@
ifeq ($(INKSCAPE_VER),0)
%.png: %.svg
inkscape --export-dpi 150 -e $@ $<
%.pdf: %.svg
inkscape -A $@ $<
else
%.png: %.svg
inkscape --export-dpi 150 --export-type png -o $@ $<
%.pdf: %.svg
inkscape --export-type pdf -o $@ $<
endif
%.pdf: %.gpi
gnuplot -e "set term pdfcairo size 5cm,5cm" $< > $@
%.pdf: %.mscgen
mscgen -T eps -o - $< | ps2pdf -dEPSCrop - $@
.PHONY: help clean html preview json epub latex latexpdf text man changes linkcheck doctest gettext
nobodd-0.4/docs/api.rst 0000664 0000000 0000000 00000004407 14572165533 0015075 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
=============
API Reference
=============
In additional to being a service, nobodd can also be used as an API from Python
to access disk images, determining their partitioning style, enumerating the
available partitions, and manipulating FAT file-systems (either from within a
disk image, or just standalone). It can also be used as the basis of a generic
TFTP service.
The following sections list the modules by their topic.
Disk Images
===========
The :class:`nobodd.disk.DiskImage` class is the primary entry-point for dealing
with disk images.
.. toctree::
:maxdepth: 1
api_disk
api_gpt
api_mbr
FAT Filesystem
==============
The :class:`nobodd.fs.FatFileSystem` class is the primary entry-point for
handling FAT file-systems.
.. toctree::
:maxdepth: 1
api_fs
api_fat
api_path
TFTP Service
============
The :class:`nobodd.tftpd.TFTPBaseServer` and
:class:`nobodd.tftpd.TFTPBaseHandler` are two classes which may be customized
to produce a TFTP server. Two example classes are included,
:class:`nobodd.tftpd.SimpleTFTPServer` and
:class:`nobodd.tftpd.SimpleTFTPHandler` which serve files directly from a
specified path.
.. toctree::
:maxdepth: 1
api_tftpd
api_tftp
api_netascii
Command line applications
=========================
The :mod:`nobodd.server` module contains the primary classes,
:class:`~nobodd.server.BootServer` and :class:`~nobodd.server.BootHandler`
which define a TFTP server (:program:`nobodd-tftpd`) that reads files from FAT
file-systems contained in OS images. The :mod:`nobodd.prep` module contains the
implementation of the :program:`nobodd-prep` command, which customizes images
prior to first net boot.
The :mod:`nobodd.config` module provides configuration parsing facilities to
these applications.
.. toctree::
:maxdepth: 1
api_server
api_prep
api_config
api_systemd
Miscellaneous
=============
The :mod:`nobodd.tools` module contains a variety of utility functions that
either cross boundaries in the system or are entirely generic.
.. toctree::
:maxdepth: 1
api_tools
nobodd-0.4/docs/api_config.rst 0000664 0000000 0000000 00000001346 14572165533 0016421 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
================
nobodd.config
================
.. module:: nobodd.config
This module contains the classes and functions used to configure the main
nobodd application. These are not likely to be of much use to other
applications, but are documented here just in case.
ConfigArgumentParser
====================
.. autoclass:: ConfigArgumentParser
Board
=====
.. autoclass:: Board
Conversion Functions
====================
.. autofunction:: port
.. autofunction:: boolean
.. autofunction:: size
.. autofunction:: duration
nobodd-0.4/docs/api_disk.rst 0000664 0000000 0000000 00000006005 14572165533 0016103 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
==============
nobodd.disk
==============
.. module:: nobodd.disk
The :mod:`nobodd.disk` module contains the :class:`DiskImage` class which is
the primary entry point for handling disk images. Constructed with a filename
(or file-like object which provides a valid :meth:`~io.IOBase.fileno` method),
the class will attempt to determine if `MBR`_ or `GPT`_ style partitioning is
in use. The :attr:`DiskImage.partitions` attribute can then be queried to
enumerate, or access the data of, individual partitions:
.. code-block:: pycon
>>> from nobodd.disk import DiskImage
>>> img = DiskImage('gpt_disk.img')
>>> img
style='gpt' signature=UUID('733b49a8-6918-4e44-8d3d-47ed9b481335')>
>>> img.style
'gpt'
>>> len(img.partitions)
4
>>> img.partitions
DiskPartitionsGPT({
1: ,
2: ,
5: ,
6: ,
})
Note that partitions are numbered from 1 and that, especially in the case of
`MBR`_, partition numbers may not be contiguous: primary partitions are
numbered 1 through 4, but logical partitions may only exist in one primary
partition, and are numbered from 5. Hence it is entirely valid to have
partitions 1, 5, and 6:
.. code-block:: pycon
>>> from nobodd.disk import DiskImage
>>> img = DiskImage('test-ebr.img')
>>> img.style
'mbr'
>>> len(img.partitions)
3
>>> list(img.partitions.keys())
[1, 5, 6]
>>> img.partitions[1]
>>> img.partitions[5]
>>> img.partitions[6]
`GPT`_ partition tables may also have non-contiguous numbering, although this
is less common in practice. The :attr:`DiskPartition.data` attribute can be
used to access the content of the partition as a buffer object (see
:class:`memoryview`).
DiskImage
=========
.. autoclass:: DiskImage
DiskPartition
=============
.. autoclass:: DiskPartition
Internal Classes
================
You should not need to use these classes directly; they will be instantiated
automatically when querying the :attr:`DiskImage.partitions` attribute
according to the detected table format.
.. autoclass:: DiskPartitionsGPT
.. autoclass:: DiskPartitionsMBR
.. _MBR: https://en.wikipedia.org/wiki/Master_boot_record
.. _GPT: https://en.wikipedia.org/wiki/GUID_Partition_Table
nobodd-0.4/docs/api_fat.rst 0000664 0000000 0000000 00000001701 14572165533 0015721 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
=============
nobodd.fat
=============
.. module:: nobodd.fat
Defines the data structures used by the `FAT`_ file system. You should never
need these directly; use the :class:`nobodd.fs.FatFileSystem` class instead.
.. _FAT: https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system
Data Structures
===============
.. autoclass:: BIOSParameterBlock
.. autoclass:: ExtendedBIOSParameterBlock
.. autoclass:: FAT32BIOSParameterBlock
.. autoclass:: FAT32InfoSector
.. autoclass:: DirectoryEntry
.. autoclass:: LongFilenameEntry
Functions
=========
These utility functions help decode certain fields within the aforementioned
structure, or check that tentative contents are valid.
.. autofunction:: lfn_checksum
.. autofunction:: lfn_valid
nobodd-0.4/docs/api_fs.rst 0000664 0000000 0000000 00000005171 14572165533 0015564 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
===============
nobodd.fs
===============
.. module:: nobodd.fs
The :mod:`nobodd.fs` module contains the :class:`FatFileSystem` class which is
the primary entry point for reading FAT file-systems. Constructed with a buffer
object representing a memory mapping of the file-system, the class will
determine whether the format is FAT12, FAT16, or FAT32. The
:attr:`~FatFileSystem.root` attribute provides a Path-like object representing
the root directory of the file-system.
.. code-block:: pycon
>>> from nobodd.disk import DiskImage
>>> from nobodd.fs import FatFileSystem
>>> img = DiskImage('test-gpt.img')
>>> fs = FatFileSystem(img.partitions[1].data)
>>> fs.fat_type
'fat16'
>>> fs.root
FatPath(, '/')
.. warning::
At the time of writing, the implementation is strictly *not thread-safe*.
Attempting to write to the file-system from multiple threads (whether in
separate instances or not) is likely to result in corruption. Attempting to
write to the file-system from one thread, while reading from another will
result in undefined behaviour including incorrect reads.
.. warning::
The implementation will *not* handle certain "obscure" extensions to FAT,
such as sub-directory style roots on FAT-12/16. It will attempt to warn
about these and abort if they are found.
FatFileSystem
=============
.. autoclass:: FatFileSystem
FatFile
=======
.. autoclass:: FatFile
Exceptions and Warnings
=======================
.. autoexception:: FatWarning
.. autoexception:: DirtyFileSystem
.. autoexception:: DamagedFileSystem
.. autoexception:: OrphanedLongFilename
.. autoexception:: BadLongFilename
Internal Classes and Functions
==============================
You should never need to interact with these classes directly; use
:class:`FatFileSystem` instead. These classes exist to enumerate and manipulate
the FAT, and different types of root directory under FAT-12, FAT-16, and
FAT-32, and sub-directories (which are common across FAT types).
.. autoclass:: FatTable
.. autoclass:: Fat12Table
.. autoclass:: Fat16Table
.. autoclass:: Fat32Table
.. autoclass:: FatClusters
.. autoclass:: FatDirectory
:members:
:private-members:
.. autoclass:: FatRoot
.. autoclass:: FatSubDirectory
.. autoclass:: Fat12Root
.. autoclass:: Fat16Root
.. autoclass:: Fat32Root
.. autofunction:: fat_type
.. autofunction:: fat_type_from_count
nobodd-0.4/docs/api_gpt.rst 0000664 0000000 0000000 00000001122 14572165533 0015736 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
=============
nobodd.gpt
=============
.. module:: nobodd.gpt
Defines the data structures used by `GUID Partition Tables`_. You should never
need these directly; use the :class:`nobodd.disk.DiskImage` class instead.
Data Structures
===============
.. autoclass:: GPTHeader
.. autoclass:: GPTPartition
.. _GUID Partition Tables: https://en.wikipedia.org/wiki/GUID_Partition_Table
nobodd-0.4/docs/api_mbr.rst 0000664 0000000 0000000 00000001145 14572165533 0015731 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
============
nobodd.mbr
============
.. module:: nobodd.mbr
Defines the data structures used by the `Master Boot Record`_ (MBR)
partitioning style. You should never need these directly; use the
:class:`nobodd.disk.DiskImage` class instead.
Data Structures
===============
.. autoclass:: MBRHeader
.. autoclass:: MBRPartition
.. _Master Boot Record: https://en.wikipedia.org/wiki/Master_boot_record
nobodd-0.4/docs/api_netascii.rst 0000664 0000000 0000000 00000003030 14572165533 0016743 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
=================
nobodd.netascii
=================
.. module:: nobodd.netascii
Registers a Python codec to translate strings to the TFTP netascii encoding
(defined in the TELNET `RFC 764`_, under the printer and keyboard section).
This is intended to translate line-endings of text files transparently between
platforms, but only handles ASCII characters.
.. note::
TFTPd implementations could *probably* ignore this as a historical artefact
at this point and assume all transfers will be done with "octet" (straight
byte for byte) encoding, as seems to be common practice. However, netascii
isn't terribly hard to support, hence the inclusion of this module.
The functions in this module should never need to be accessed directly. Simply
use the 'netascii' encoding as you would any other Python byte-encoding:
.. code-block:: pycon
>>> import os
>>> os.linesep
'\n'
>>> import nobodd.netascii
>>> 'foo\nbar\r'.encode('netascii')
b'foo\r\nbar\r\0'
>>> b'foo\r\nbar\r\0\r\r'.decode('netascii', errors='replace')
'foo\nbar\r??'
.. _RFC 764: https://datatracker.ietf.org/doc/html/rfc764
Internal Functions
==================
.. autofunction:: encode
.. autofunction:: decode
.. autoclass:: IncrementalEncoder
.. autoclass:: IncrementalDecoder
.. autoclass:: StreamWriter
.. autoclass:: StreamReader
nobodd-0.4/docs/api_path.rst 0000664 0000000 0000000 00000002602 14572165533 0016104 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
============
nobodd.path
============
.. module:: nobodd.path
Defines the :class:`FatPath` class, a Path-like class for interacting with
directories and sub-directories in a :class:`~nobodd.fs.FatFileSystem`
instance. You should never need to construct this class directly; instead it
should be derived from the :attr:`~nobodd.fs.FatFileSystem.root` attribute
which is itself a :class:`FatPath` instance.
.. code-block:: pycon
>>> from nobodd.disk import DiskImage
>>> from nobodd.fs import FatFileSystem
>>> img = DiskImage('test.img')
>>> fs = FatFileSystem(img.partitions[1].data)
>>> for p in fs.root.iterdir():
... print(repr(p))
...
FatPath(, '/foo')
FatPath(, '/bar.txt')
FatPath(, '/setup.cfg')
FatPath(, '/baz')
FatPath(, '/adir')
FatPath(, '/BDIR')
FatPath
=======
.. autoclass:: FatPath
Internal Functions
==================
.. autofunction:: get_cluster
nobodd-0.4/docs/api_prep.rst 0000664 0000000 0000000 00000001170 14572165533 0016115 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
=============
nobodd.prep
=============
.. module:: nobodd.prep
This module contains the implementation (and entry point) of the
:program:`nobodd-prep` application.
Application Functions
=====================
.. autofunction:: main
.. autofunction:: get_parser
.. autofunction:: prepare_image
.. autofunction:: remove_items
.. autofunction:: copy_items
.. autofunction:: rewrite_cmdline
.. autofunction:: detect_partitions
nobodd-0.4/docs/api_server.rst 0000664 0000000 0000000 00000001433 14572165533 0016457 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
==============
nobodd.server
==============
.. module:: nobodd.server
This module contains the server and handler classes which make up the main
:program:`nobodd-tftpd` application, as well as the entry point for the
application itself.
Handler Classes
===============
.. autoclass:: BootHandler
Server Classes
==============
.. autoclass:: BootServer
Application Functions
=====================
.. autofunction:: main
.. autofunction:: request_loop
.. autofunction:: get_parser
Exceptions
==========
.. autoexception:: ReloadRequest
.. autoexception:: TerminateRequest
nobodd-0.4/docs/api_systemd.rst 0000664 0000000 0000000 00000001603 14572165533 0016640 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
===============
nobodd.systemd
===============
.. module:: nobodd.systemd
This module contains a singleton class intended for communication with the
:manpage:`systemd(1)` service manager. It includes facilities for running a
service as ``Type=notify`` where the service can actively communicate to
systemd that it is ready to handle requests, is reloading its configuration, is
shutting down, or that it needs more time to handle certain operations.
It also includes methods to ping the systemd watchdog, and to retrieve
file-descriptors stored on behalf of the service (or provided as part of
socket-activation).
Systemd Class
=============
.. autoclass:: Systemd
.. autofunction:: get_systemd
nobodd-0.4/docs/api_tftp.rst 0000664 0000000 0000000 00000003244 14572165533 0016130 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
==============
nobodd.tftp
==============
.. module:: nobodd.tftp
Defines the data structures used by the `Trivial File Transfer Protocol`_
(TFTP). You should never need these directly; use the classes in
:mod:`nobodd.tftpd` to construct a TFTP server instead.
Enumerations
============
.. autoclass:: OpCode
.. autoclass:: Error
Constants
=========
.. data:: TFTP_BLKSIZE
.. data:: TFTP_MIN_BLKSIZE
.. data:: TFTP_DEF_BLKSIZE
.. data:: TFTP_MAX_BLKSIZE
Constants defining the ``blksize`` TFTP option; the name of the option, its
minimum, default, and maximum values.
.. data:: TFTP_TIMEOUT
.. data:: TFTP_UTIMEOUT
.. data:: TFTP_MIN_TIMEOUT_NS
.. data:: TFTP_DEF_TIMEOUT_NS
.. data:: TFTP_MAX_TIMEOUT_NS
Constants defining the ``timeout`` and ``utimeout`` TFTP options; the name
of the options, the minimum, default, and maximum values, in units of
nano-seconds.
.. data:: TFTP_BINARY
.. data:: TFTP_NETASCII
.. data:: TFTP_MODES
Constants defining the available transfer modes.
.. data:: TFTP_TSIZE
Constant defining the name of the ``tsize`` TFTP option.
.. data:: TFTP_OPTIONS
Constant defining the TFTP options available for negotiation.
Packets
=======
.. autoclass:: Packet
.. autoclass:: RRQPacket
.. autoclass:: WRQPacket
.. autoclass:: DATAPacket
.. autoclass:: ACKPacket
.. autoclass:: ERRORPacket
.. autoclass:: OACKPacket
.. _Trivial File Transfer Protocol: https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol
nobodd-0.4/docs/api_tftpd.rst 0000664 0000000 0000000 00000005460 14572165533 0016276 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
=============
nobodd.tftpd
=============
.. module:: nobodd.tftpd
Defines several classes for the purposes of constructing TFTP servers. The most
useful are :class:`TFTPBaseHandler` and :class:`TFTPBaseServer` which are
abstract base classes for the construction of a TFTP server with an arbitrary
source of files (these are used by nobodd's :mod:`~nobodd.main` module). In
addition, :class:`TFTPSimplerHandler` and :class:`TFTPSimplerServer` are
provided as a trivial example implementation of a straight-forward TFTP file
server.
For example, to start a TFTP server which will serve files from the current
directory on (unprivileged) port 1069:
.. code-block:: pycon
>>> from nobodd.tftpd import SimpleTFTPServer
>>> server = SimpleTFTPServer(('0.0.0.0', 1069), '.')
>>> server.serve_forever()
Handler Classes
===============
.. autoclass:: TFTPBaseHandler
.. autoclass:: SimpleTFTPHandler
Server Classes
==============
.. autoclass:: TFTPBaseServer
.. autoclass:: SimpleTFTPServer
Command Line Use
================
Just as :mod:`http.server` can be invoked from the command line as a standalone
server using the interpreter's :option:`-m` option, so :mod:`nobodd.tftpd` can
too. To serve the current directory as a TFTP server::
python -m nobodd.tftpd
The server listens to port 6969 by default. This is not the registered port 69
of TFTP, but as that port requires root privileges by default on UNIX
platforms, a safer default was selected (the security provenance of this code
is largely unknown, and certainly untested at higher privilege levels). The
default port can be overridden by passed the desired port number as an
argument::
python -m nobodd.tftpd 1069
By default, the server binds to all interfaces. The option ``-b/--bind``
specifies an address to which it should bind instead. Both IPv4 and IPv6
addresses are supported. For example, the following command causes the server
to bind to localhost only::
python -m nobodd.tftpd --bind 127.0.0.1
By default, the server uses the current directory. The option
``-d/--directory`` specifies a directory from which it should serve files
instead. For example::
python -m nobodd.tftpd --directory /tmp/
Internal Classes and Exceptions
===============================
The following classes and exceptions are entirely for internal use and should
never be needed (directly) by applications.
.. autoclass:: TFTPClientState
.. autoclass:: TFTPHandler
.. autoclass:: TFTPSubHandler
.. autoclass:: TFTPSubServer
.. autoclass:: TFTPSubServers
.. autoexception:: TransferDone
.. autoexception:: AlreadyAcknowledged
.. autoexception:: BadOptions
nobodd-0.4/docs/api_tools.rst 0000664 0000000 0000000 00000001605 14572165533 0016312 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
===============
nobodd.tools
===============
.. module:: nobodd.tools
This module houses a series of miscellaneous functions which did not fit
particularly well anywhere else and are needed across a variety of modules.
They should never be needed by developers using nobodd as an application or a
library, but are documented in case they are useful.
.. autofunction:: labels
.. autofunction:: formats
.. autofunction:: get_best_family
.. autofunction:: format_address
.. autofunction:: pairwise
.. autofunction:: decode_timestamp
.. autofunction:: encode_timestamp
.. autofunction:: any_match
.. autofunction:: exclude
.. autoclass:: BufferedTranscoder
.. autoclass:: FrozenDict
nobodd-0.4/docs/changelog.rst 0000664 0000000 0000000 00000001605 14572165533 0016250 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
==================
Changelog
==================
.. currentmodule:: nobodd
Release 0.4 (2024-03-07)
========================
* Use absolute paths for output of nbd-server and tftpd server configurations
* Include missing ``#cloud-config`` header in the tutorial
Release 0.3 (2024-03-06)
========================
* Fix configuration reload when inheriting the TFTP socket from a service
manager (`#8`_)
.. _#8: https://github.com/waveform80/nobodd/issues/8
Prototype 0.2 (unreleased)
==========================
* Add inheritance of the TFTP socket (`#3`_)
.. _#3: https://github.com/waveform80/nobodd/issues/3
Prototype 0.1 (unreleased)
==========================
* Initial tag
nobodd-0.4/docs/cli.rst 0000664 0000000 0000000 00000000600 14572165533 0015062 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
===============
CLI Reference
===============
The following chapters document the command line utilities included in nobodd:
.. toctree::
:maxdepth: 1
cli_prep
cli_server
nobodd-0.4/docs/cli_prep.rst 0000664 0000000 0000000 00000025115 14572165533 0016120 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
.. include:: subst.rst
=============
nobodd-prep
=============
Customizes an OS image to prepare it for netbooting via TFTP. Specifically,
this expands the image to a specified size (the assumption being the image is a
copy of a minimally sized template image), then updates the kernel command line
on the boot partition to point to an NBD server.
Synopsis
========
.. code-block:: text
usage: nobodd-prep [-h] [--version] [-s SIZE] [--nbd-host HOST]
[--nbd-name NAME] [--cmdline NAME]
[--boot-partition NUM] [--root-partition NUM]
[-C PATH] [-R PATH] image
Options
=======
.. program:: nobodd-prep
.. option:: image
The target image to customize
.. option:: -h, --help
show the help message and exit
.. option:: --version
show program's version number and exit
.. option:: -s SIZE, --size SIZE
The size to expand the image to; default: 16GB
.. option:: --nbd-host HOST
The hostname of the nbd server to connect to for the root device; defaults
to the local machine's FQDN
.. option:: --nbd-name NAME
The name of the nbd share to use as the root device; defaults to the stem
of the *image* name
.. option:: --cmdline NAME
The name of the file containing the kernel command line on the boot
partition; default: :file:`cmdline.txt`
.. option:: --boot-partition NUM
Which partition is the boot partition within the image; default is the
first FAT partition (identified by partition type) found in the image
.. option:: --root-partition NUM
Which partition is the root partition within the image default is the first
non-FAT partition (identified by partition type) found in the image
.. option:: -C PATH, --copy PATH
Copy the specified file or directory into the boot partition. This may be
given multiple times to specify multiple items to copy
.. option:: -R PATH, --remove PATH
Delete the specified file or directory within the boot partition. This may
be given multiple times to specify multiple items to delete
.. option:: --serial HEX
Defines the serial number of the Raspberry Pi that will be served this
image. When this option is given, a board configuration compatible with
:program:`nobodd-tftpd` may be output with :option:`--tftpd-conf`
.. option:: --tftpd-conf FILE
If specified, write a board configuration compatible with
:program:`nobodd-tftpd` to the specified file; requires :option:`--serial`
to be given. If "-" is given, output is written to stdout.
.. option:: --nbd-conf FILE
If specified, write a share configuration compatible with
:manpage:`nbd-server(1)` to the specified file. If "-" is given, output is
written to stdout.
Usage
=====
Typically :program:`nobodd-prep` is called with a base OS image. For example,
if :file:`ubuntu-24.04-server.img.xz` is the Ubuntu 24.04 Server for Raspberry
image, we would decompress it (we can only work on uncompressed images), use
the tool to expand it to a reasonable disk size (e.g. 16GB like an SD card),
and customize the kernel command line to look for the rootfs on our NBD server:
.. code-block:: console
$ ls -l ubuntu-24.04-server.img.xz
-rw-rw-r-- 1 dave dave 1189280360 Oct 12 00:44 ubuntu-24.04-server.img.xz
$ unxz ubuntu-24.04-server.img.xz
$ ls -l ubuntu-24.04-server.img
-rw-rw-r-- 1 dave dave 3727687680 Oct 12 00:44 ubuntu-24.04-server.img
$ fdisk -l ubuntu-24.04-server.img
Disk ubuntu-24.04-server.img: 3.47 GiB, 3727687680 bytes, 7280640 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x1634ec00
Device Boot Start End Sectors Size Id Type
ubuntu-24.04-server.img1 * 2048 1050623 1048576 512M c W95 FAT32 (LBA)
ubuntu-24.04-server.img2 1050624 7247259 6196636 3G 83 Linux
$ mkdir mnt
$ sudo mount -o loop,offset=$((2048*512)),sizelimit=$((1048576*512)) ubuntu-24.04-server.img mnt/
[sudo] Password:
$ cat mnt/cmdline.txt
console=serial0,115200 multipath=off dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc
$ sudo umount mnt/
$ nobodd-prep --size 16GB ubuntu-24.04-server.img
$ ls -l ubuntu-24.04-server.img --nbd-host myserver --nbd-name ubuntu
-rw-rw-r-- 1 dave dave 17179869184 Feb 27 13:11 ubuntu-24.04-server.img
$ sudo mount -o loop,offset=$((2048*512)),sizelimit=$((1048576*512)) ubuntu-24.04-server.img mnt/
[sudo] Password:
$ cat mnt/cmdline.txt
ip=dhcp nbdroot=myserver/ubuntu root=/dev/nbd0p2 console=serial0,115200 multipath=off dwc_otg.lpm_enable=0 console=tty1 rootfstype=ext4 rootwait fixrtc
$ sudo umount mnt/
Note, the only reason we are listing partitions and mounting the boot partition
above is to demonstrate the change to the kernel command line in
:file:`cmdline.txt`. Ordinarily, usage of :program:`nobodd-prep` is as simple
as:
.. code-block:: console
$ unxz ubuntu-24.04-server.img.xz
$ nobodd-prep --size 16GB ubuntu-24.04-server.img
Typically :program:`nobodd-prep` will detect the boot and root partitions of
the image automatically. The boot partition is defined as the first partition
that has a FAT `partition type`_ (on `MBR-partitioned`_ images), or `Basic
Data`_ or `EFI System`_ partition type (on `GPT-partitioned`_ images), which
contains a valid FAT file-system (the script tries to determine the FAT-type of
the contained file-system, and only counts those partitions on which it can
determine a valid FAT-type).
The root partition is the exact opposite; it is defined as the first partition
that *doesn't* have a FAT `partition type`_ (on `MBR-partitioned`_ images), or
`Basic Data`_ or `EFI System`_ partition type (on `GPT-partitioned`_ images),
which contains something *other than* a valid FAT file-system (again, the
script tries to determine the FAT-type of the contained file-system, and only
counts those partitions on which it *cannot* determine a valid FAT-type).
There may be images for which these simplistic definitions do not work. For
example, images derived from a `NOOBS/PINN`_ install may well have several boot
partitions for different installed OS'. In this case the boot or root partition
(or both) may be specified manually on the command line:
.. code-block:: console
$ fdisk -l pinn-test.img
Disk pinn-test.img: 29.72 GiB, 31914983424 bytes, 62333952 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x2e779525
Device Boot Start End Sectors Size Id Type
pinn-test.img1 8192 137215 129024 63M e W95 FAT16 (LBA)
pinn-test.img2 137216 62333951 62196736 29.7G 5 Extended
pinn-test.img5 139264 204797 65534 32M 83 Linux
pinn-test.img6 204800 464895 260096 127M c W95 FAT32 (LBA)
pinn-test.img7 466944 4661247 4194304 2G 83 Linux
pinn-test.img8 4669440 5193727 524288 256M 83 Linux
pinn-test.img9 5201920 34480125 29278206 14G 83 Linux
pinn-test.img10 34480128 34998271 518144 253M c W95 FAT32 (LBA)
pinn-test.img11 35004416 62333951 27329536 13G 83 Linux
$ nobodd-prep --boot-partition 10 --root-partition 11 pinn-test.img
:program:`nobodd-prep` also includes several facilities for customizing the
boot partition beyond re-writing the kernel's :file:`cmdline.txt`.
Specifically, the :option:`--remove` and :option:`--copy` options.
The :option:`--remove` option can be given multiple times, and tells
:program:`nobodd-prep` to remove the specified files or directories from the
boot partition. The :option:`--copy` option can also be given multiple times,
and tells :program:`nobodd-prep` to copy the specified files or directories
into the root of the boot partition. In both cases, directories that are
specified are removed or copied recursively.
The :option:`--copy` option is particularly useful for overwriting the
`cloud-init`_ seeds on the boot partition of Ubuntu Server images, in case you
want to provide an initial network configuration, user setup, or list of
packages to install on first boot:
.. code-block:: console
$ cat user-data
chpasswd:
expire: true
users:
- name: ubuntu
password: raspberry
type: text
ssh_pwauth: false
package_update: true
package_upgrade: true
packages:
- avahi-daemon
$ nobodd-prep --copy user-data ubuntu-24.04-server.img
There is no need to :option:`--remove` files you wish to :option:`--copy`; the
latter option will overwrite where necessary. The exception to this is copying
directories; if you are copying a directory that already exists in the boot
partition, the new content will be merged with the existing content. Files
under the directory that share a name will be overwritten, files that do not
will be left in place. If you wish to replace the directory wholesale, specify
it with :option:`--remove` as well.
The ordering of options on the command line does *not* affect the order of
operations in the utility. The order of operations in :program:`nobodd-prep` is
strictly as follows:
1. Detect partitions, if necessary
2. Re-size the image, if necessary
3. Remove all items on the boot partition specified by :option:`--remove`
4. Copy all items specified by :option:`--copy` into the boot partition
5. Re-write the ``root=`` option in the :file:`cmdline.txt` file
This ordering is deliberate, firstly to ensure directories can be replaced (as
noted above), and secondly to ensure :file:`cmdline.txt` can be customized by
:option:`--copy` prior to the customization performed by the utility.
See Also
========
.. only:: not man
:doc:`cli_server`, :manpage:`nbd-server(1)`
.. only:: man
:manpage:`nobodd-tftpd(1)`, :manpage:`nbd-server(1)`
Bugs
====
|bug-link|
.. _partition type: https://en.wikipedia.org/wiki/Partition_type
.. _MBR-partitioned: https://en.wikipedia.org/wiki/Master_boot_record
.. _GPT-partitioned: https://en.wikipedia.org/wiki/GUID_Partition_Table
.. _Basic Data: https://en.wikipedia.org/wiki/Microsoft_basic_data_partition
.. _EFI System: https://en.wikipedia.org/wiki/EFI_system_partition
.. _NOOBS/PINN: https://github.com/procount/pinn
.. _cloud-init: https://cloudinit.readthedocs.io/en/latest/
nobodd-0.4/docs/cli_server.rst 0000664 0000000 0000000 00000013775 14572165533 0016471 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
.. include:: subst.rst
=============
nobodd-tftpd
=============
A read-only TFTP server capable of reading FAT boot partitions from within
image files or devices. Intended to be paired with a block-device service (e.g.
NBD) for netbooting Raspberry Pis.
Synopsis
========
.. code-block:: text
usage: nobodd-tftpd [-h] [--version] [--listen ADDR] [--port PORT]
[--board SERIAL,FILENAME[,PART[,IP]]]
Options
=======
.. program:: nobodd-tftpd
.. option:: -h, --help
show the help message and exit
.. option:: --version
show program's version number and exit
.. option:: --board SERIAL,FILENAME[,PART[,IP]]
can be specified multiple times to define boards which are to be served
boot images over TFTP; if PART is omitted the default is 1; if IP is
omitted the IP address will not be checked
.. option:: --listen ADDR
the address on which to listen for connections (default: "::" for all
addresses)
.. option:: --port PORT
the port on which to listen for connections (default: "tftp" which is port
69)
Configuration
=============
:program:`nobodd-tftpd` can be configured via the command line, or from several
configuration files. These are structured as INI-style files with bracketed
``[sections]`` containing ``key=value`` lines, and optionally #-prefixed
comments. The configuration files which are read, and the order they are
consulted is as follows:
1. :file:`/etc/nobodd/nobodd.conf`
2. :file:`/usr/local/etc/nobodd/nobodd.conf`
3. :file:`$XDG_CONFIG_HOME/nobodd/nobodd.conf` (where ``$XDG_CONFIG_HOME``
defaults to :file:`~/.config` if unset)
Later files override settings from files earlier in this order.
The configuration file may contain a ``[tftp]`` section which may contain the
following values:
listen
This is equivalent to the :option:`--listen` parameter and specifies the
address(es) on which the server will listen for incoming TFTP connections.
port
This is equivalent to the :option:`--port` parameter and specifies the UDP
port on which the server will listen for incoming TFTP connections. Please
note that only the *initial* TFTP packet will arrive on this port. Each
"connection" is allocated its own `ephemeral port`_ on the server and all
subsequent packets will use this ephemeral port.
includedir
If this is specified, it provides the name of a directory which will be
scanned for files matching the pattern :file:`*.conf`. Any files found
matching will be read as additional configuration files, in sorted filename
order.
For example:
.. code-block:: ini
[tftp]
listen = 192.168.0.0/16
port = tftp
includedir = /etc/nobodd/conf.d
For each image the TFTP server is expected to serve to a Raspberry Pi, a
``[board:SERIAL]`` section should be defined. Here, "SERIAL" should be replaced
by the serial number of the Raspberry Pi. The serial number can be found in the
output of ``cat /proc/cpuinfo`` at runtime. For example:
.. code-block:: console
$ grep ^Serial /proc/cpuinfo
Serial : 100000001234abcd
If the serial number starts with 10000000 (as in the example above), exclude
the initial one and all leading zeros. So the above Pi has a serial number of
1234abcd (in hexadecimal). Within the section the following values are valid:
image
Specifies the full path to the operating system image to serve to the
specified Pi, presumably prepared with :program:`nobodd-prep`.
partition
Optionally specifies the number of the boot partition. If this is not
specified it defaults to 1.
ip
Optionally limits serving any files from this image unless the IP address
of the client matches. If this is not specified, any IP address may
retrieve files from this share.
For example:
.. code-block:: ini
[board:1234abcd]
image = /srv/images/ubuntu-24.04-server.img
partition = 1
ip = 192.168.0.5
In practice, what this means is that requests from a client with the IP address
"192.168.0.5", for files under the path "1234abcd/", will be served from the
FAT file-system on partition 1 of the image stored at
:file:`/srv/images/ubuntu-24.04-server.img`.
Such definitions can be produced by :program:`nobodd-prep` when it is provided
with the :option:`nobodd-prep --serial` option.
Boards may also be defined on the command-line with the :option:`--board`
option. These definitions will augment (and override, where the serial number
is identical) those definitions provided by the configuration files.
Systemd/Inetd Usage
===================
The server may inherit its listening socket from a managing process. In the
case of :manpage:`inetd(8)` where the listening socket is traditionally passed
as stdin (fd 0), pass "stdin" as the value of :option:`--listen` (or the
``listen`` option within the ``[tftp]`` section of the configuration file).
In the case of :manpage:`systemd(1)`, where the listening socket(s) are passed
via the environment, specify "systemd" as the value of :option:`--listen` (or
the ``listen`` option within the ``[tftp]`` section of the configuration file)
and the service will expect to find a single socket passed in
:envvar:`LISTEN_FDS`. This will happen implicitly if the service is declared as
socket-activated. However, the service must *not* use ``Accept=yes`` as the
TFTP protocol is connection-less. The example units provided in the source code
demonstrate using socket-activation with the server.
In both cases, the service manager sets the port that the service will listen
on, so the :option:`--port` option (and the ``port`` option in the ``[tftp]``
section of the configuration file) is silently ignored.
See Also
========
.. only:: not man
:doc:`cli_prep`, :manpage:`nbd-server(1)`
.. only:: man
:manpage:`nobodd-prep(1)`, :manpage:`nbd-server(1)`
Bugs
====
|bug-link|
.. _ephemeral port: https://en.wikipedia.org/wiki/Ephemeral_port
nobodd-0.4/docs/conf.py 0000664 0000000 0000000 00000006365 14572165533 0015076 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python3
# vim: set fileencoding=utf-8:
#
# nobodd: a boot configuration tool for the Raspberry Pi
#
# Copyright (c) 2023-2024 Dave Jones
# Copyright (c) 2023-2024 Canonical Ltd.
#
# SPDX-License-Identifier: GPL-3.0
import sys
import os
import configparser
from pathlib import Path
from datetime import datetime
from setuptools.config import read_configuration
on_rtd = os.environ.get('READTHEDOCS', '').lower() == 'true'
config = configparser.ConfigParser()
config.read([Path(__file__).parent / '..' / 'setup.cfg'])
info = config['metadata']
# -- Project information -----------------------------------------------------
project = info['name']
author = info['author']
now = datetime.now()
copyright = (
f'2023-{now:%Y} {author}' if now.year > 2023 else f'2023 {author}')
release = info['version']
version = release
# -- General configuration ------------------------------------------------
needs_sphinx = '4.0'
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.intersphinx',
'sphinx.ext.imgmath',
]
if on_rtd:
tags.add('rtd')
root_doc = 'index'
templates_path = ['_templates']
exclude_patterns = ['_build']
highlight_language = 'python3'
pygments_style = 'sphinx'
# -- Autodoc configuration ------------------------------------------------
autodoc_member_order = 'groupwise'
autodoc_default_options = {
'members': True,
}
autodoc_mock_imports = []
# -- Intersphinx configuration --------------------------------------------
intersphinx_mapping = {
'python': ('https://docs.python.org/3.12', None),
}
# -- Options for HTML output ----------------------------------------------
html_theme = 'sphinx_rtd_theme'
html_title = f'{project} {version} Documentation'
html_static_path = ['_static']
manpages_url = 'https://manpages.ubuntu.com/manpages/noble/en/man{section}/{page}.{section}.html'
# -- Options for LaTeX output ---------------------------------------------
latex_engine = 'xelatex'
latex_elements = {
'papersize': 'a4paper',
'pointsize': '10pt',
'preamble': r'\def\thempfootnote{\arabic{mpfootnote}}', # workaround sphinx issue #2530
}
latex_documents = [
(
'index', # source start file
project + '.tex', # target filename
html_title, # title
author, # author
'manual', # documentclass
True, # documents ref'd from toctree only
),
]
latex_show_pagerefs = True
latex_show_urls = 'footnote'
# -- Options for epub output ----------------------------------------------
epub_basename = project
epub_author = author
epub_identifier = f'https://{info["name"]}.readthedocs.io/'
epub_show_urls = 'no'
# -- Options for manual page output ---------------------------------------
man_pages = [
(
'cli_server',
'nobodd-tftpd',
'nobodd-tftpd - serve boot partition files over TFTP',
[info['author']],
1,
),
(
'cli_prep',
'nobodd-prep',
'nobodd-prep - prepare an OS image for NBD netboot',
[info['author']],
1,
),
]
man_show_urls = True
# -- Options for linkcheck builder ----------------------------------------
linkcheck_retries = 3
linkcheck_workers = 20
linkcheck_anchors = True
nobodd-0.4/docs/development.rst 0000664 0000000 0000000 00000010125 14572165533 0016640 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2023-2024 Dave Jones
.. Copyright (c) 2023-2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
===========
Development
===========
.. currentmodule:: nobodd
The main GitHub repository for the project can be found at:
https://github.com/waveform80/nobodd
.. _dev_install:
Development installation
========================
If you wish to develop nobodd, obtain the source by cloning the GitHub
repository and then use the "develop" target of the Makefile which will install
the package as a link to the cloned repository allowing in-place development.
The following example demonstrates this method within a virtual Python
environment:
.. code-block:: console
$ sudo apt install build-essential git virtualenvwrapper
After installing ``virtualenvwrapper`` you'll need to restart your shell before
commands like :command:`mkvirtualenv` will operate correctly. Once you've
restarted your shell, continue:
.. code-block:: console
$ cd
$ mkvirtualenv nobodd
$ workon nobodd
(nobodd) $ git clone https://github.com/waveform80/nobodd.git
(nobodd) $ cd nobodd
(nobodd) $ make develop
To pull the latest changes from git into your clone and update your
installation:
.. code-block:: console
$ workon nobodd
(nobodd) $ cd ~/nobodd
(nobodd) $ git pull
(nobodd) $ make develop
To remove your installation, destroy the sandbox and the clone:
.. code-block:: console
(nobodd) $ deactivate
$ rmvirtualenv nobodd
$ rm -rf ~/nobodd
Building the docs
=================
If you wish to build the docs, you'll need a few more dependencies. Inkscape
is used for conversion of SVGs to other formats, Graphviz is used for rendering
certain charts, and TeX Live is required for building PDF output. The following
command should install all required dependencies:
.. code-block:: console
$ sudo apt install texlive-latex-recommended texlive-latex-extra \
texlive-fonts-recommended texlive-xetex graphviz inkscape \
python3-sphinx python3-sphinx-rtd-theme latexmk xindy
Once these are installed, you can use the "doc" target to build the
documentation in all supported formats (HTML, ePub, and PDF):
.. code-block:: console
$ workon nobodd
(nobodd) $ cd ~/nobodd
(nobodd) $ make doc
However, the easiest way to develop the documentation is with the "preview"
target which will build the HTML version of the docs, and start a web-server to
preview the output. The web-server will then watch for source changes (in both
the documentation source, and the application's source) and rebuild the HTML
automatically as required:
.. code-block:: console
$ workon nobodd
(nobodd) $ cd ~/nobodd
(nobodd) $ make preview
The HTML output is written to :file:`build/html` while the PDF output
goes to :file:`build/latex`.
Test suite
==========
If you wish to run the nobodd test suite, follow the instructions in
:ref:`dev_install` above and then make the "test" target within the sandbox:
.. code-block:: console
$ workon nobodd
(nobodd) $ cd ~/nobodd
(nobodd) $ make test
The test suite is also setup for usage with the :command:`tox` utility, in
which case it will attempt to execute the test suite with all supported
versions of Python. If you are developing under Ubuntu you may wish to look
into the `Dead Snakes PPA`_ in order to install old/new versions of Python; the
tox setup *should* work with the version of tox shipped with Ubuntu Focal, but
more features (like parallel test execution) are available with later versions.
For example, to execute the test suite under tox, skipping interpreter versions
which are not installed:
.. code-block:: console
$ tox
To execute the test suite under all installed interpreter versions in parallel,
using as many parallel tasks as there are CPUs, then displaying a combined
report of coverage from all environments:
.. code-block:: console
$ tox -p auto
$ coverage combine .coverage.py*
$ coverage report
.. _Dead Snakes PPA: https://launchpad.net/~deadsnakes/%2Barchive/ubuntu/ppa
nobodd-0.4/docs/explain-pi-netboot.rst 0000664 0000000 0000000 00000021421 14572165533 0020035 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
=================
Netboot on the Pi
=================
In order to understand nobodd, it is useful to understand the netboot procedure
on the Raspberry Pi in general. At a high level, it consists of three phases
which we'll cover in the following sections.
DHCP
====
The first phase is quite simply a fairly typical `DHCP` phase, in which the
bootloader attempts to obtain an IPv4 address from the local :abbr:`DHCP
(Dynamic Host Configuration Protocol)` server. On the Pi 4 (and later models),
the address obtained can be seen on the boot diagnostics screen. Near the top
the line starting with "net:" indicates the current network status. Initially
this will read::
net: down ip: 0.0.0.0 sn: 0.0.0.0 gw: 0.0.0.0
Shortly before attempting netboot, this line should change to something like
the following::
net: up ip: 192.168.1.137 sn: 255.255.255.0 gw: 192.168.1.1
This indicates that the Pi has obtained the address "192.168.1.137" on a class
D subnet ("192.168.1.0/24" in `CIDR`_ form), and knows the local network
gateway is at "192.168.1.1".
The bootloader also inspects certain DHCP options to locate the `TFTP`_ server
for the next phase. Specifically:
* DHCP option 66 (TFTP server) can specify the address directly
* If DHCP option 43 (vendor options) specifies PXE string "Raspberry Pi Boot"
[#pxe_id]_ then option 54 (server identifier) will be used
* On the Pi 4 (and later), the EEPROM can override both of these with the
`TFTP_IP`_ option
With the network configured, and the TFTP server address obtained, we move onto
the TFTP phase...
TFTP
====
.. TODO Updated bootcode.bin on earlier models? Test on the 2+3
.. note::
Most of the notes under this section are specific, in some way, to the
netboot sequence on the Pi 4. While older and newer models may broadly
follow the same sequence, there will be differences.
The bootloader's `TFTP`_ client first attempts to locate the :file:`start4.elf`
file. By default, it looks for this in a directory named after the Pi's serial
number. On the Pi 4 and later models, the EEPROM configuration can override
this behaviour with the `TFTP_PREFIX`_ option, but we will only cover the
default behaviour here.
All subsequent files will be requested from within this serial number directory
prefix [#no-prefix]_. Hence, when we say the bootloader requests
:file:`SERIAL/vmlinuz`, we mean it requests the file :file:`vmlinuz` from
within the virtual directory named after the Pi's serial number
[#long-serial]_.
The attempt to retrieve :file:`start4.elf` is immediately aborted when it is
located, presumably because the intent is to determine the existence of the
prefix directory, rather than the file itself. Next the bootloader attempts to
read :file:`SERIAL/config.txt`, which will configure the rest of the boot
sequence.
Once :file:`SERIAL/config.txt` has been retrieved, the bootloader parses it to
discover the name of the tertiary bootloader to load [#pi5-eeprom]_, and
requests :file:`SERIAL/start.elf` or :file:`SERIAL/start4.elf` (depending on
the model) and the corresponding fix-up file (:file:`SERIAL/fixup.dat` or
:file:`SERIAL/fixup4.dat` respectively).
The bootloader now executes the tertiary "start.elf" bootloader which requests
:file:`SERIAL/config.txt` again. This is re-parsed [#sections]_ and the name of
the base device-tree, kernel, kernel command line, (optional) initramfs, and
any (optional) device-tree overlays are determined. These are then requested
over TFTP, placed in RAM, and finally the bootloader hands over control to the
kernel.
TFTP Extensions
---------------
A brief aside on the subject of :abbr:`TFTP (Trivial File Transfer Protocol)`
extensions (as defined in :rfc:`2347`). The basic TFTP protocol is extremely
simple (as the acronym would suggest) and also rather inefficient, being limited
to 512-byte blocks, in-order, synchronously (each block must be acknowledged
before another can be sent), with no retry mechanism. Various extensions have
been proposed to the protocol over the years, including those in :rfc:`2347`,
:rfc:`2348`, :rfc:`2349`, and :rfc:`7440`.
The Pi bootloader implements *some* of these extensions. Specifically, it uses
the "blocksize" extension (:rfc:`2348`) to negotiate a larger size of block to
transfer, and the "tsize" extension (:rfc:`2349`) to attempt to determine the
size of a transfer prior to it beginning.
However, its use of "tsize" is slightly unusual in that, when it finds the
server supports it, it frequently starts a transfer with "tsize=0" (requesting
the size of the file), but when the server responds with, for example,
"tsize=1234" in the OACK packet (indicating the file to be transferred is 1234
bytes large), the bootloader then terminates the transfer.
In the case of the initial request for :file:`start4.elf` (detailed above),
this is understandable as a test for the existence of a directory, rather than
an actual attempt to retrieve a file. However, in later requests the bootloader
terminates the transfer after the initial packet, *then immediately restarts
it*. My best guess is that it allocates the RAM for the transfer after the
termination, then restarts it (though why it does this is a bit of a mystery as
it could allocate the space and continue the transfer, since the OACK packet
doesn't contain any of the file data itself).
Sadly, the "windowsize" extension (:rfc:`7440`) is not yet implemented which
means the Pi's netboot, up to the kernel, is quite slow compared to other
methods.
Kernel
======
The kernel is now running with the configured command line, and (optionally)
the address of an initial ramdisk (initramfs) as the root file-system. The
initramfs is expected to contain the relevant kernel modules, and client
binaries to talk to whatever network server will provide the root file-system.
Traditionally on the Raspberry Pi, this has meant `NFS`_. However, it may also
be `NBD`_ (as served by :manpage:`nbd-server(1)`) or `iSCSI`_ (as served by
:manpage:`iscsid(8)`). Typically, the ``init`` process loaded from the kernel's
initramfs will dissect the kernel's command line to determine the location of
the root file-system, and mount it using the appropriate utilities.
In the case of :manpage:`nbd-server(1)` the following items in the kernel
command line are crucial:
* ``ip=dhcp`` tells the kernel that it should request an IP address via DHCP
(the Pi's bootloader cannot pass network state to the kernel, so this must be
re-done)
* ``nbdroot=HOST/SHARE`` tells the kernel that it should open "SHARE" on the
NBD server at HOST. This will form the block device ``/dev/nbd0``
* ``root=/dev/nbd0p2`` tells the kernel that the root file-system is on the
second partition of the block device
.. _DHCP: https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol
.. _CIDR: https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
.. _TFTP: https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol
.. _TFTP_IP: https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#TFTP_IP
.. _TFTP_PREFIX: https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#TFTP_IP
.. _NFS: https://en.wikipedia.org/wiki/Network_File_System
.. _NBD: https://en.wikipedia.org/wiki/Network_block_device
.. _iSCSI: https://en.wikipedia.org/wiki/ISCSI
.. [#pxe_id] In early versions of the Raspberry Pi bootloader, the string
needed to include three trailing spaces, i.e. ``"Raspberry Pi Boot "``.
Later versions of the bootloader perform a sub-string match.
.. [#no-prefix] If :file:`start4.elf` is not found in the serial-number
directory, the bootloader will attempt to lovate :file:`start4.elf` with no
directory prefix. If this succeeds, all subsequent requests will have no
serial-number directory prefix.
.. [#long-serial] Some Pi serial numbers begin "10000000". This prefix is
ignored for the purposes of constructing the serial-number directory prefix.
For example, if the serial number is "10000000abcd1234", the
:file:`config.txt` file would be requested as :file:`abcd1234/config.txt`.
.. [#pi5-eeprom] This does not happen on the Pi 5, which loads the tertiary
bootloader from its (larger) EEPROM. On all prior models, the tertiary
bootloader (start*.elf) loads from the boot medium, and the specific file
loaded may be customized by :file:`config.txt`.
.. This does not happen *by default* on the Pi 5? Need to investigate further
.. [#sections] The tertiary bootloader operates on all ``[sections]`` in the
:file:`config.txt`. The secondary bootloader (:file:`bootcode.bin`) only
operates on some of these and doesn't comprehend the full syntax that the
tertiary bootloader does (for instance, the secondary bootloader won't
handle includes).
nobodd-0.4/docs/explanations.rst 0000664 0000000 0000000 00000000642 14572165533 0017026 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
===============
Explanations
===============
The following chapter(s) contain explanations that may aid understanding of
Raspberry Pi's netboot process in general.
.. toctree::
:maxdepth: 1
explain-pi-netboot
nobodd-0.4/docs/howto-firewall.rst 0000664 0000000 0000000 00000005004 14572165533 0017261 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
===================================
How to firewall your netboot server
===================================
If you wish to add a netfilter (or iptables) firewall to your server running
nobodd and nbd-server, there are a few things to be aware of.
The `NBD`_ protocol is quite trivial to firewall; the protocol uses TCP and
listens on a single port: 10809. Hence, adding a rule that allows "NEW" inbound
TCP connections on port 10809, and a rule to permit traffic on "ESTABLISHED"
connections is generally sufficient (where "NEW" and "ESTABLISHED" have their
typical meanings in netfilter's connection state tracking).
The `TFTP`_ protocol is, theoretically at least, a little harder. The TFTP
protocol uses UDP (i.e. it's connectionless) and though it starts on the
`privileged port`_ 69, this is only the case for the initial in-bound packet.
All subsequent packets in a transfer take place on an ephemeral port on both
the client *and the server* [#tid]_ .
Hence, a typical transfer looks like this:
.. image:: images/tftp-basic.*
Thankfully, because the server sends the initial response from its ephemeral
port, and the client replies to that ephemeral port, it will also count as
"ESTABLISHED" traffic in netfilter's parlance. Hence, all that's required to
successfully firewall the TFTP side is to permit "NEW" inbound packets on port
69, and to permit "ESTABLISHED" UDP packets.
Putting this altogether, a typical :manpage:`iptables(8)` sequence might look
like this:
.. code-block:: console
$ sudo -i
[sudo] Password:
# iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
# iptables -A INPUT -p tcp -m state --state NEW --dport 10809 -j ACCEPT
# iptables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
# iptables -A INPUT -p udp -m state --state NEW --dport 69 -j ACCEPT
.. _TFTP: https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol
.. _NBD: https://en.wikipedia.org/wiki/Network_block_device
.. _privileged port: https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports
.. [#tid] transfers are uniquely identified by the tuple of the client's
ephemeral port, and the server's ephemeral port; this ensures a client may
have multiple simultaneous transfers even in the case of a degenerate client
that initiates multiple simultaneous transfers from a single port
nobodd-0.4/docs/howto-jammy.rst 0000664 0000000 0000000 00000007754 14572165533 0016607 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
.. Once ubuntu-image works reliably, this should be re-written to just
.. "generate your custom Ubuntu image, manually seeding nbd-client and
.. modules-extra with this handy yaml!"
===========================
How to netboot Ubuntu 22.04
===========================
The Ubuntu 22.04 (jammy) images are not compatible with NBD boot out of the box
as they lack the ``nbd-client`` package in their seed. However, you can modify
the image to make it compatible.
On the Pi
=========
Fire up `rpi-imager`_ and flash Ubuntu 22.04.4 server onto an SD card, then
boot that SD card on your Pi (the model does not matter provided it can boot
the image).
.. warning::
Do *not* be tempted to upgrade packages at this point. Specifically, the
kernel package must *not* be upgraded yet.
Install the ``linux-modules-extra-raspi`` package for the currently running
kernel version, and the ``nbd-client`` package.
.. code-block:: console
$ sudo apt install linux-modules-extra-$(uname -r) nbd-client
On Ubuntu versions prior to 24.04, the ``nbd`` kernel module was moved out of
the default ``linux-modules-raspi`` package for efficiency. We specifically
need the version matching the running kernel version because installing this
package will regenerate the initramfs (``initrd.img``). We'll be copying that
regenerated file into the image we're going to netboot and it *must* match the
kernel version in that image. This is why it was important not to upgrade any
packages after the first boot.
We also need to install the NBD client package to add the ``nbd-client``
executable to the initramfs, along with some scripts to call it if the kernel
command line specifies an NBD device as root:
We copy the regenerated ``initrd.img`` to the server, and shut down the Pi.
Adjust the ``ubuntu@server`` reference below to fit your user on your server.
.. code-block:: console
$ scp -q /boot/firmware/initrd.img ubuntu@server:
$ sudo poweroff
On the Server
=============
Download the same OS image to your server, verify its content, unpack it, and
rename it to something more reasonable.
.. code-block:: console
$ wget http://cdimage.ubuntu.com/releases/22.04.4/release/ubuntu-22.04.4-preinstalled-server-arm64+raspi.img.xz
...
$ wget http://cdimage.ubuntu.com/releases/22.04.4/release/SHA256SUMS
...
$ sha256sum --check --ignore-missing SHA256SUMS
ubuntu-22.04.4-preinstalled-server-arm64+raspi.img.xz: OK
$ rm SHA256SUMS
$ mv ubuntu-22.04.4-preinstalled-server-arm64+raspi.img jammy.img
Next we need to create a cloud-init configuration which will perform the same
steps we performed earlier on the first boot of our fresh image, namely to
install ``nbd-client`` and ``linux-modules-extra-raspi``, alongside the usual
user configuration.
.. code-block:: console
$ cat << EOF > user-data
#cloud-config
chpasswd:
expire: true
users:
- name: ubuntu
password: ubuntu
type: text
ssh_pwauth: false
package_update: true
packages:
- nbd-client
- linux-modules-extra-raspi
EOF
See the `cloud-init documentation`_, a `this series of blog posts
`_ for more ideas on what can be done with the
:file:`user-data` file.
Preparing the Image
===================
When preparing our image with :program:`nobodd-prep` we must remember to copy
in our ``user-data`` and ``initrd.img`` files, overwriting the ones on the boot
partition.
.. code-block:: console
$ nobodd-prep --size 16GB --copy initrd.img --copy user-data jammy.img
At this point you should have a variant of the Ubuntu 22.04 image that is
capable of being netbooted over NBD.
.. _rpi-imager: https://www.raspberrypi.com/software/
.. _cloud-init documentation: https://cloudinit.readthedocs.io/
.. _waldorf-cloud-init: https://waldorf.waveform.org.uk/tag/cloud-init.html
nobodd-0.4/docs/howtos.rst 0000664 0000000 0000000 00000000671 14572165533 0015646 0 ustar 00root root 0000000 0000000 .. nobodd: a boot configuration tool for the Raspberry Pi
..
.. Copyright (c) 2024 Dave Jones
.. Copyright (c) 2024 Canonical Ltd.
..
.. SPDX-License-Identifier: GPL-3.0
==============
How To Guides
==============
The following guides cover specific, but commonly encountered, circumstances in
operating a Raspberry Pi netboot server using NBD.
.. toctree::
:maxdepth: 1
howto-jammy
howto-firewall
nobodd-0.4/docs/images/ 0000775 0000000 0000000 00000000000 14572165533 0015032 5 ustar 00root root 0000000 0000000 nobodd-0.4/docs/images/tftp-basic.mscgen 0000664 0000000 0000000 00000001322 14572165533 0020262 0 ustar 00root root 0000000 0000000 msc {
hscale = "0.8", arcgradient = "15";
"client:ephemeral","server:69","server:ephemeral";
"client:ephemeral"=>"server:69" [label="RRQ(filename)"];
"server:69"->"server:ephemeral" [label="alloc port"];
"server:ephemeral"=>"client:ephemeral" [label="OACK"];
"client:ephemeral"=>"server:ephemeral" [label="ACK(0)"];
"server:ephemeral"=>"client:ephemeral" [label="DATA(1)"];
"client:ephemeral"=>"server:ephemeral" [label="ACK(1)"];
"server:ephemeral"=>"client:ephemeral" [label="DATA(2)"];
"client:ephemeral"=>"server:ephemeral" [label="ACK(2)"];
...;
"server:ephemeral"=>"client:ephemeral" [label="DATA(n)"];
"client:ephemeral"=>"server:ephemeral" [label="ACK(n)"];
}
nobodd-0.4/docs/images/tftp-basic.png 0000664 0000000 0000000 00000120611 14572165533 0017575 0 ustar 00root root 0000000 0000000 PNG
IHDR " pHYs &? tEXtSoftware www.inkscape.org< IDATxy\T?0
^J)~lhRj?]sI-\EqOE+AE̙s=3Ι;7p?H5 vnٌFjFP'"v YN$Z
Tq@BFIv#ʛpPNHJJK/(<