Initial import
This commit is contained in:
commit
9ac667e68d
340
COPYING
Normal file
340
COPYING
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) 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
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: 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
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
227
ChangeLog
Normal file
227
ChangeLog
Normal file
@ -0,0 +1,227 @@
|
||||
5.20 - Released 2005-05-04
|
||||
- pkgadd/rejmerge will now consider user, group and access
|
||||
permissions on rejected files.
|
||||
|
||||
5.19 - Released 2005-03-29
|
||||
- pkgadd: improved support for automatically removing
|
||||
rejected files that are identical to already installed files.
|
||||
- pkgmk: added support for resuming interrupted downloads.
|
||||
Thanks to Johannes Winkelmann <jw@tks6.net>
|
||||
- pkgmk: added option -cm/--check-md5sum, which checks the
|
||||
md5sum but does not build the package.
|
||||
- libtar: fixed bug in symlink handling.
|
||||
Thanks to Guillaume Bibaut <guillaume.bibaut@free.fr>
|
||||
|
||||
5.18 - Released 2004-05-16
|
||||
- rejmerge: files created when merging will now get the same
|
||||
access permissions as the installed version.
|
||||
Thanks to Han Boetes <han@mijncomputer.nl>
|
||||
- rejmerge: file diffs/merges are now piped through more(1).
|
||||
- pkgadd/pkgrm: fixed a bug that could result in a corrupt
|
||||
database when running low on disk space.
|
||||
- pkgadd: directories can now be specified in rules in
|
||||
pkgadd.conf. (This fix was supposed to be part of the 5.14
|
||||
release, but was forgotten and actually never included).
|
||||
|
||||
5.17 - Released 2004-05-10
|
||||
- Fixed two bugs in rejmerge.
|
||||
|
||||
5.16 - Released 2004-05-09
|
||||
- pkgmk no longer redirects errors to /dev/null when removing
|
||||
the work dir.
|
||||
- Minor man page updates.
|
||||
|
||||
5.15 - Released 2004-05-02
|
||||
- Fixed bug in makefile.
|
||||
|
||||
5.14 - Released 2004-05-02
|
||||
- Added new utility called rejmerge.
|
||||
See rejmerge(8) man page for more information.
|
||||
- pkginfo -o now accepts regular expressions.
|
||||
- Directories can now be specified in rules in pkgadd.conf.
|
||||
- pkgadd/pkgrm now executes ldconfig after installing/removing
|
||||
a package.
|
||||
- Minor cleanups.
|
||||
|
||||
5.13 - Released 2003-12-16
|
||||
- Removed "link to ..." from .footprint.
|
||||
- pkgmk now allows the source=() array to be empty. This
|
||||
is useful for packages that only want create directory
|
||||
structures and/or symlinks.
|
||||
|
||||
5.12 - Released 2003-11-30
|
||||
- Added support for .nostrip, an optional file containing
|
||||
regular expressions matching files that should not be
|
||||
stripped. Thanks to Dave Hatton <mail@davehatton.it>
|
||||
|
||||
5.11 - Released 2003-11-27
|
||||
- Fixed bug in footprint generation.
|
||||
- Fixed bug in file stripping.
|
||||
|
||||
5.10 - Released 2003-11-08
|
||||
- pkginfo: Added option -f/--footprint, which generates a
|
||||
package footprint. The old method for generating footprints
|
||||
failed in special cases.
|
||||
- pkgmk: Updated to use pkginfo -f when creating footprints.
|
||||
- pkgmk: Fixed bug in man page compression.
|
||||
- pkgmk: Removed support for ROOT in Pkgfiles, use PKGMK_ROOT
|
||||
instead.
|
||||
- pkgmk: Removed support for SOURCE_DIR, PACKAGE_DIR and
|
||||
WORK_DIR, use PKGMK_SOURCE_DIR, PKGMK_PACKAGE_DIR and
|
||||
PKGMK_WORK_DIR instead.
|
||||
|
||||
5.9 - Released 2003-10-19
|
||||
- Fixed bug in database backup code.
|
||||
- Rejected files that are empty or equal to the already
|
||||
installed version are now automatically removed.
|
||||
|
||||
5.8 - Released 2003-10-03
|
||||
- Fixed memory leak in pkgadd.
|
||||
- Patched libtar to fix memory leak.
|
||||
- Patched libtar to reduce memory usage.
|
||||
- Updated default pkgadd.conf.
|
||||
|
||||
5.7 - Released 2003-07-31
|
||||
- pkgmk: Reintroduced the $ROOT variable.
|
||||
|
||||
5.6 - Released 2003-07-05
|
||||
- pkgmk: Added automatic stripping of libraries (can be
|
||||
disabled with -ns/--no-strip).
|
||||
- pkgmk: Added option -if/--ignore-footprint, which builds a
|
||||
package without checking the footprint.
|
||||
- pkgmk: Synchronized names of variables exposed in pkgmk.conf
|
||||
to avoid potential conflicts. All variables now start with
|
||||
PKGMK_. The old names (SOURCE_DIR, PACKAGE_DIR and WORK_DIR)
|
||||
still work but this backwards compatibility will be removed
|
||||
in the future.
|
||||
|
||||
5.5 - Released 2003-05-03
|
||||
- pkgmk: Added support for alternative source, package and work
|
||||
directories. Variables SOURCE_DIR, PACKAGE_DIR and WORK_DIR
|
||||
can be set in /etc/pkgmk.conf.
|
||||
Thanks to Markus Ackermann <maol@symlink.ch>.
|
||||
- Minor changes to some info/error messages.
|
||||
|
||||
5.4 - Released 2003-03-09
|
||||
- pkgmk: Added option -c/--clean, which removes the package
|
||||
and the downloaded source files.
|
||||
- Upgraded bundled libtar from 1.2.10 to 1.2.11. This
|
||||
version of libtar fixes a spurious "permission denied"
|
||||
error, which sometimes occurred when running "pkgadd -u".
|
||||
|
||||
5.3 - Released 2003-02-05
|
||||
- pkgadd: the combination of -f and -u now respects the
|
||||
upgrade configuration in /etc/pkgadd.conf. This is
|
||||
needed to better support upgrades where ownership of
|
||||
files has been moved from one package to another.
|
||||
- pkgadd/pkgrm/pkginfo: improved/reworked database locking
|
||||
and error handling.
|
||||
- pkgmk: added -o to unzip to make it behave more like tar
|
||||
and avoid user intaraction when overwriting files.
|
||||
Thanks to Andreas Sundström <sunkan@zappa.cx>.
|
||||
- Upgraded bundled libtar from 1.2.9 to 1.2.10.
|
||||
|
||||
5.2 - Released 2002-12-08
|
||||
- pkgmk: exports LC_ALL=POSIX to force utilities to use a
|
||||
neutral locate.
|
||||
- Upgraded bundled libtar from 1.2.8 to 1.2.9.
|
||||
|
||||
5.1 - Released 2002-10-27
|
||||
- Upgraded bundled libtar from 1.2.5 to 1.2.8.
|
||||
- pkgadd/pkgrm/pkginfo: Added file-locking on database to
|
||||
prevent more than one instance of pkgadd/pkgrm/pkginfo from
|
||||
running at the same time.
|
||||
- pkgadd: Fixed a bug in libtar that caused segmentation fault
|
||||
when extracting files whose filenames contains characters
|
||||
with ascii value > 127.
|
||||
- pkgmk: Fixed bug which caused suid/sgid binaries to become
|
||||
unstripped.
|
||||
- pkgmk: Added option -ns/--no-strip. Use it to avoid stripping
|
||||
binaries in a package.
|
||||
- pkginfo: -o/--owner does not require the whole path to the
|
||||
file anymore.
|
||||
|
||||
5.0 - Released 2002-09-09
|
||||
- Now requires GCC 3.2 to compile (due to STL incompatibility).
|
||||
- pkginfo: -o/--owner now prepends the current directory to
|
||||
the file argument unless it starts with /. This feature is
|
||||
disable when using the -r/--root option.
|
||||
- pkgmk: The build() function will now be aborted as soon
|
||||
as some command exits with an exit code other than 0 (zero).
|
||||
- pkgmk: Binaries are now stripped automatically.
|
||||
- pkgmk: Man pages are now compressed automatically.
|
||||
- pkgmk: Symlinks are always given access permissions
|
||||
lrwxrwxrwx in .footprint, regardless of the actual
|
||||
access permissions. This avoids footprint problems
|
||||
when using e.g. XFS.
|
||||
|
||||
4.4 - Released 2002-06-30
|
||||
- Added option -cf, --config-file to pkgmk.
|
||||
- Minor bugfixes.
|
||||
|
||||
4.3 - Released 2002-06-11
|
||||
- Removed Pkgfile.local-feature which was added in 4.2. It
|
||||
didn't work very well in some (common) situations.
|
||||
- Corrected spelling errors in pkgmk.
|
||||
|
||||
4.2 - Released 2002-05-17
|
||||
- Added support for Pkgfile.local, which enables users to
|
||||
tweak packages by overriding parts of the original
|
||||
Pkgfile. This is useful when pkgmk is used in CRUX's
|
||||
ports system, where users will loose changes made to the
|
||||
original Pkgfile the next time they update their ports
|
||||
structure.
|
||||
- Minor cleanups.
|
||||
|
||||
4.1 - Released 2002-04-08
|
||||
- Added support for preventing selected files (typically
|
||||
configuration files) from being overwritten when upgrading
|
||||
a package. The file /etc/pkgadd.conf, contains a list of
|
||||
rules with regular expressions specifying these files. These
|
||||
rules will be consulted when executing pkgadd with the
|
||||
option -u. Files that, according to the rules, shouldn't be
|
||||
upgraded will instead be installed under
|
||||
/var/lib/pkg/rejected/. The user can then examine, use and
|
||||
remove these files manually if so desired.
|
||||
- Added md5sum checking (.md5sum contains the MD5 checksum of
|
||||
all source files). pkgmk uses this file to verify that
|
||||
the (potentially downloaded) source files are correct.
|
||||
- Upgraded bundled libtar from 1.2.4 to 1.2.5.
|
||||
|
||||
4.0.1 - Released 2002-01-20
|
||||
- Removed warning "unable to remove XXX: Directory not empty"
|
||||
when upgrading a package.
|
||||
|
||||
4.0 - Released 2002-01-14
|
||||
- Packages are now identified by their names only (and
|
||||
not by name and version as before). This makes it easier
|
||||
for users to upgrade and remove packages. This, of course,
|
||||
comes with a price. You can not install two packages with
|
||||
the same name.
|
||||
- The naming convention for packages is now:
|
||||
name#version-release.pkg.tar.gz
|
||||
The character '#' is not allowed in package names, since
|
||||
it's used as the name/version delimiter.
|
||||
- New database layout, which gives a more robust database
|
||||
with a transaction-like behaviour. This implementation
|
||||
will gurantee that the database will never be corrupted
|
||||
even if the power fails when pkgadd/pkgrm is running. It
|
||||
does however not guarantee that the database contents is
|
||||
in sync with the filesystem if such a crash should occur.
|
||||
This means that the database will _never_ loose track of
|
||||
files that are installed, but it can (in case of a crash)
|
||||
contain files that are actually not installed. Repeating
|
||||
the pkgadd/pkgrm command that was running when the crash
|
||||
occured will get the database in sync with the filesystem
|
||||
again.
|
||||
- pkgmk is now capable of downloading missing source files
|
||||
(using wget) before building a package (option -d), given
|
||||
that the URL is specified in the "source" variable.
|
||||
- pkg.build was renamed to Pkgfile (to mimic make/Makefile).
|
||||
- pkg.contents was renamed to .footprint.
|
||||
- pkgmk is now capable of installing/upgrading a package if
|
||||
the build was successful (option -i and -u).
|
||||
- Lot's of minor fixes and cleanups.
|
||||
|
||||
0.1 - 3.2.0 - Released 2000-05-10 - 2001-10-03
|
||||
(No change log was maintained during this time)
|
111
Makefile
Normal file
111
Makefile
Normal file
@ -0,0 +1,111 @@
|
||||
#
|
||||
# pkgutils
|
||||
#
|
||||
# Copyright (c) 2000-2005 by Per Liden <per@fukt.bth.se>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
# USA.
|
||||
#
|
||||
|
||||
DESTDIR =
|
||||
BINDIR = /usr/bin
|
||||
MANDIR = /usr/man
|
||||
ETCDIR = /etc
|
||||
|
||||
VERSION = 5.21
|
||||
LIBTAR_VERSION = 1.2.11
|
||||
|
||||
CXXFLAGS += -DNDEBUG
|
||||
CXXFLAGS += -O2 -Wall -pedantic -D_GNU_SOURCE -DVERSION=\"$(VERSION)\" \
|
||||
-Ilibtar-$(LIBTAR_VERSION)/lib -Ilibtar-$(LIBTAR_VERSION)/listhash
|
||||
|
||||
LDFLAGS += -static -Llibtar-$(LIBTAR_VERSION)/lib -ltar -lz
|
||||
|
||||
OBJECTS = main.o pkgutil.o pkgadd.o pkgrm.o pkginfo.o
|
||||
|
||||
MANPAGES = pkgadd.8 pkgrm.8 pkginfo.8 pkgmk.8 rejmerge.8
|
||||
|
||||
LIBTAR = libtar-$(LIBTAR_VERSION)/lib/libtar.a
|
||||
|
||||
all: pkgadd pkgmk rejmerge man
|
||||
|
||||
$(LIBTAR):
|
||||
(tar xzf libtar-$(LIBTAR_VERSION).tar.gz; \
|
||||
cd libtar-$(LIBTAR_VERSION); \
|
||||
patch -p1 < ../libtar-$(LIBTAR_VERSION)-fix_mem_leak.patch; \
|
||||
patch -p1 < ../libtar-$(LIBTAR_VERSION)-reduce_mem_usage.patch; \
|
||||
patch -p1 < ../libtar-$(LIBTAR_VERSION)-fix_linkname_overflow.patch; \
|
||||
LDFLAGS="" ./configure --disable-encap --disable-encap-install; \
|
||||
make)
|
||||
|
||||
pkgadd: $(LIBTAR) .depend $(OBJECTS)
|
||||
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS)
|
||||
|
||||
pkgmk: pkgmk.in
|
||||
|
||||
rejmerge: rejmerge.in
|
||||
|
||||
man: $(MANPAGES)
|
||||
|
||||
mantxt: man $(MANPAGES:=.txt)
|
||||
|
||||
%.8.txt: %.8
|
||||
nroff -mandoc -c $< | col -bx > $@
|
||||
|
||||
%: %.in
|
||||
sed -e "s/#VERSION#/$(VERSION)/" $< > $@
|
||||
|
||||
.depend:
|
||||
$(CXX) $(CXXFLAGS) -MM $(OBJECTS:.o=.cc) > .depend
|
||||
|
||||
ifeq (.depend,$(wildcard .depend))
|
||||
include .depend
|
||||
endif
|
||||
|
||||
.PHONY: install clean distclean dist
|
||||
|
||||
dist: distclean
|
||||
rm -rf /tmp/pkgutils-$(VERSION)
|
||||
mkdir -p /tmp/pkgutils-$(VERSION)
|
||||
cp -rf . /tmp/pkgutils-$(VERSION)
|
||||
tar -C /tmp --exclude .svn -czvf ../pkgutils-$(VERSION).tar.gz pkgutils-$(VERSION)
|
||||
rm -rf /tmp/pkgutils-$(VERSION)
|
||||
|
||||
install: all
|
||||
install -D -m0755 pkgadd $(DESTDIR)$(BINDIR)/pkgadd
|
||||
install -D -m0644 pkgadd.conf $(DESTDIR)$(ETCDIR)/pkgadd.conf
|
||||
install -D -m0755 pkgmk $(DESTDIR)$(BINDIR)/pkgmk
|
||||
install -D -m0755 rejmerge $(DESTDIR)$(BINDIR)/rejmerge
|
||||
install -D -m0644 pkgmk.conf $(DESTDIR)$(ETCDIR)/pkgmk.conf
|
||||
install -D -m0644 rejmerge.conf $(DESTDIR)$(ETCDIR)/rejmerge.conf
|
||||
install -D -m0644 pkgadd.8 $(DESTDIR)$(MANDIR)/man8/pkgadd.8
|
||||
install -D -m0644 pkgrm.8 $(DESTDIR)$(MANDIR)/man8/pkgrm.8
|
||||
install -D -m0644 pkginfo.8 $(DESTDIR)$(MANDIR)/man8/pkginfo.8
|
||||
install -D -m0644 pkgmk.8 $(DESTDIR)$(MANDIR)/man8/pkgmk.8
|
||||
install -D -m0644 rejmerge.8 $(DESTDIR)$(MANDIR)/man8/rejmerge.8
|
||||
ln -sf pkgadd $(DESTDIR)$(BINDIR)/pkgrm
|
||||
ln -sf pkgadd $(DESTDIR)$(BINDIR)/pkginfo
|
||||
|
||||
clean:
|
||||
rm -f .depend
|
||||
rm -f $(OBJECTS)
|
||||
rm -f $(MANPAGES)
|
||||
rm -f $(MANPAGES:=.txt)
|
||||
|
||||
distclean: clean
|
||||
rm -f pkgadd pkginfo pkgrm pkgmk rejmerge
|
||||
rm -rf libtar-$(LIBTAR_VERSION)
|
||||
|
||||
# End of file
|
28
README
Normal file
28
README
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
pkgutils - Package Management Utilities
|
||||
|
||||
http://www.fukt.bth.se/~per/pkgutils/
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
pkgutils is a set of utilities (pkgadd, pkgrm, pkginfo, pkgmk and rejmerge),
|
||||
which are used for managing software packages in Linux. It is developed for
|
||||
and used by the CRUX distribution (http://crux.nu).
|
||||
|
||||
|
||||
Building and installing
|
||||
-----------------------
|
||||
$ make
|
||||
$ make install
|
||||
or
|
||||
$ make DESTDIR=/some/other/path install
|
||||
|
||||
|
||||
Copyright
|
||||
---------
|
||||
pkgutils is Copyright (c) 2000-2005 Per Liden and is licensed through the
|
||||
GNU General Public License. Read the COPYING file for the complete license.
|
||||
|
||||
pkgutils uses libtar, a library for reading/writing tar-files. This
|
||||
library is Copyright (c) 1998-2003 Mark D. Roth.
|
35
libtar-1.2.11-fix_linkname_overflow.patch
Normal file
35
libtar-1.2.11-fix_linkname_overflow.patch
Normal file
@ -0,0 +1,35 @@
|
||||
diff -ru libtar-1.2.11/lib/decode.c libtar-1.2.11-new/lib/decode.c
|
||||
--- libtar-1.2.11/lib/decode.c 2004-08-18 22:12:06.888107160 +0200
|
||||
+++ libtar-1.2.11-new/lib/decode.c 2004-08-18 22:05:27.569812768 +0200
|
||||
@@ -42,6 +42,17 @@
|
||||
return filename;
|
||||
}
|
||||
|
||||
+char*
|
||||
+th_get_linkname(TAR* t)
|
||||
+{
|
||||
+ static char filename[MAXPATHLEN];
|
||||
+
|
||||
+ if (t->th_buf.gnu_longlink)
|
||||
+ return t->th_buf.gnu_longlink;
|
||||
+
|
||||
+ snprintf(filename, sizeof(filename), "%.100s", t->th_buf.linkname);
|
||||
+ return filename;
|
||||
+}
|
||||
|
||||
uid_t
|
||||
th_get_uid(TAR *t)
|
||||
diff -ru libtar-1.2.11/lib/libtar.h libtar-1.2.11-new/lib/libtar.h
|
||||
--- libtar-1.2.11/lib/libtar.h 2003-01-07 02:40:59.000000000 +0100
|
||||
+++ libtar-1.2.11-new/lib/libtar.h 2004-08-18 21:59:12.344855632 +0200
|
||||
@@ -184,9 +184,7 @@
|
||||
#define th_get_mtime(t) oct_to_int((t)->th_buf.mtime)
|
||||
#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor)
|
||||
#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor)
|
||||
-#define th_get_linkname(t) ((t)->th_buf.gnu_longlink \
|
||||
- ? (t)->th_buf.gnu_longlink \
|
||||
- : (t)->th_buf.linkname)
|
||||
+char *th_get_linkname(TAR *t);
|
||||
char *th_get_pathname(TAR *t);
|
||||
mode_t th_get_mode(TAR *t);
|
||||
uid_t th_get_uid(TAR *t);
|
26
libtar-1.2.11-fix_mem_leak.patch
Normal file
26
libtar-1.2.11-fix_mem_leak.patch
Normal file
@ -0,0 +1,26 @@
|
||||
diff -ru libtar-1.2.11/lib/decode.c libtar-1.2.11-new/lib/decode.c
|
||||
--- libtar-1.2.11/lib/decode.c 2003-01-07 02:40:59.000000000 +0100
|
||||
+++ libtar-1.2.11-new/lib/decode.c 2003-10-03 15:02:44.000000000 +0200
|
||||
@@ -26,7 +26,7 @@
|
||||
char *
|
||||
th_get_pathname(TAR *t)
|
||||
{
|
||||
- char filename[MAXPATHLEN];
|
||||
+ static char filename[MAXPATHLEN];
|
||||
|
||||
if (t->th_buf.gnu_longname)
|
||||
return t->th_buf.gnu_longname;
|
||||
@@ -35,11 +35,11 @@
|
||||
{
|
||||
snprintf(filename, sizeof(filename), "%.155s/%.100s",
|
||||
t->th_buf.prefix, t->th_buf.name);
|
||||
- return strdup(filename);
|
||||
+ return filename;
|
||||
}
|
||||
|
||||
snprintf(filename, sizeof(filename), "%.100s", t->th_buf.name);
|
||||
- return strdup(filename);
|
||||
+ return filename;
|
||||
}
|
||||
|
||||
|
66
libtar-1.2.11-reduce_mem_usage.patch
Normal file
66
libtar-1.2.11-reduce_mem_usage.patch
Normal file
@ -0,0 +1,66 @@
|
||||
diff -ru libtar-1.2.11/lib/extract.c libtar-1.2.11-new/lib/extract.c
|
||||
--- libtar-1.2.11/lib/extract.c 2003-03-03 00:58:07.000000000 +0100
|
||||
+++ libtar-1.2.11-new/lib/extract.c 2003-10-03 15:07:46.000000000 +0200
|
||||
@@ -28,14 +28,6 @@
|
||||
#endif
|
||||
|
||||
|
||||
-struct linkname
|
||||
-{
|
||||
- char ln_save[MAXPATHLEN];
|
||||
- char ln_real[MAXPATHLEN];
|
||||
-};
|
||||
-typedef struct linkname linkname_t;
|
||||
-
|
||||
-
|
||||
static int
|
||||
tar_set_file_perms(TAR *t, char *realname)
|
||||
{
|
||||
@@ -98,7 +90,9 @@
|
||||
tar_extract_file(TAR *t, char *realname)
|
||||
{
|
||||
int i;
|
||||
- linkname_t *lnp;
|
||||
+ char *lnp;
|
||||
+ int pathname_len;
|
||||
+ int realname_len;
|
||||
|
||||
if (t->options & TAR_NOOVERWRITE)
|
||||
{
|
||||
@@ -137,11 +131,13 @@
|
||||
if (i != 0)
|
||||
return i;
|
||||
|
||||
- lnp = (linkname_t *)calloc(1, sizeof(linkname_t));
|
||||
+ pathname_len = strlen(th_get_pathname(t)) + 1;
|
||||
+ realname_len = strlen(realname) + 1;
|
||||
+ lnp = (char *)calloc(1, pathname_len + realname_len);
|
||||
if (lnp == NULL)
|
||||
return -1;
|
||||
- strlcpy(lnp->ln_save, th_get_pathname(t), sizeof(lnp->ln_save));
|
||||
- strlcpy(lnp->ln_real, realname, sizeof(lnp->ln_real));
|
||||
+ strcpy(&lnp[0], th_get_pathname(t));
|
||||
+ strcpy(&lnp[pathname_len], realname);
|
||||
#ifdef DEBUG
|
||||
printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
|
||||
"value=\"%s\"\n", th_get_pathname(t), realname);
|
||||
@@ -288,7 +284,7 @@
|
||||
{
|
||||
char *filename;
|
||||
char *linktgt = NULL;
|
||||
- linkname_t *lnp;
|
||||
+ char *lnp;
|
||||
libtar_hashptr_t hp;
|
||||
|
||||
if (!TH_ISLNK(t))
|
||||
@@ -304,8 +300,8 @@
|
||||
if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
|
||||
(libtar_matchfunc_t)libtar_str_match) != 0)
|
||||
{
|
||||
- lnp = (linkname_t *)libtar_hashptr_data(&hp);
|
||||
- linktgt = lnp->ln_real;
|
||||
+ lnp = (char *)libtar_hashptr_data(&hp);
|
||||
+ linktgt = &lnp[strlen(lnp) + 1];
|
||||
}
|
||||
else
|
||||
linktgt = th_get_linkname(t);
|
BIN
libtar-1.2.11.tar.gz
Normal file
BIN
libtar-1.2.11.tar.gz
Normal file
Binary file not shown.
76
main.cc
Normal file
76
main.cc
Normal file
@ -0,0 +1,76 @@
|
||||
//
|
||||
// pkgutils
|
||||
//
|
||||
// Copyright (c) 2000-2005 Per Liden
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
// USA.
|
||||
//
|
||||
|
||||
#if (__GNUC__ < 3)
|
||||
#error This program requires GCC 3.x to compile.
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdlib>
|
||||
#include <libgen.h>
|
||||
#include "pkgutil.h"
|
||||
#include "pkgadd.h"
|
||||
#include "pkgrm.h"
|
||||
#include "pkginfo.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static pkgutil* select_utility(const string& name)
|
||||
{
|
||||
if (name == "pkgadd")
|
||||
return new pkgadd;
|
||||
else if (name == "pkgrm")
|
||||
return new pkgrm;
|
||||
else if (name == "pkginfo")
|
||||
return new pkginfo;
|
||||
else
|
||||
throw runtime_error("command not supported by pkgutils");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
string name = basename(argv[0]);
|
||||
|
||||
try {
|
||||
auto_ptr<pkgutil> util(select_utility(name));
|
||||
|
||||
// Handle common options
|
||||
for (int i = 1; i < argc; i++) {
|
||||
string option(argv[i]);
|
||||
if (option == "-v" || option == "--version") {
|
||||
util->print_version();
|
||||
return EXIT_SUCCESS;
|
||||
} else if (option == "-h" || option == "--help") {
|
||||
util->print_help();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
util->run(argc, argv);
|
||||
} catch (runtime_error& e) {
|
||||
cerr << name << ": " << e.what() << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
68
pkgadd.8.in
Normal file
68
pkgadd.8.in
Normal file
@ -0,0 +1,68 @@
|
||||
.TH pkgadd 8 "" "pkgutils #VERSION#" ""
|
||||
.SH NAME
|
||||
pkgadd \- install software package
|
||||
.SH SYNOPSIS
|
||||
\fBpkgadd [options] <file>\fP
|
||||
.SH DESCRIPTION
|
||||
\fBpkgadd\fP is a \fIpackage management\fP utility, which installs
|
||||
a software package. A \fIpackage\fP is an archive of files (.pkg.tar.gz).
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B "\-u, \-\-upgrade"
|
||||
Upgrade/replace package with the same name as <file>.
|
||||
.TP
|
||||
.B "\-f, \-\-force"
|
||||
Force installation, overwrite conflicting files. If the package
|
||||
that is about to be installed contains files that are already
|
||||
installed this option will cause all those files to be overwritten.
|
||||
This option should be used with care, preferably not at all.
|
||||
.TP
|
||||
.B "\-r, \-\-root <path>"
|
||||
Specify alternative installation root (default is "/"). This
|
||||
should \fInot\fP be used as a way to install software into
|
||||
e.g. /usr/local instead of /usr. Instead this should be used
|
||||
if you want to install a package on a temporary mounted partition,
|
||||
which is "owned" by another system. By using this option you not only
|
||||
specify where the software should be installed, but you also
|
||||
specify which package database to use.
|
||||
.TP
|
||||
.B "\-v, \-\-version"
|
||||
Print version and exit.
|
||||
.TP
|
||||
.B "\-h, \-\-help"
|
||||
Print help and exit.
|
||||
.SH CONFIGURATION
|
||||
When using \fBpkgadd\fP in upgrade mode (i.e. option -u is used) the
|
||||
file \fI/etc/pkgadd.conf\fP will be read. This file can contain rules describing
|
||||
how pkgadd should behave when doing upgrades. A rule is built out of three
|
||||
fragments, \fIevent\fP, \fIpattern\fP and \fIaction\fP. The event describes
|
||||
in what kind of situation this rule applies. Currently only one type of event is
|
||||
supported, that is \fBUPGRADE\fP. The pattern is a regular expression and the action
|
||||
applicable to the \fBUPGRADE\fP event is \fBYES\fP and \fBNO\fP. More than one rule of the same
|
||||
event type is allowed, in which case the first rule will have the lowest priority and the last rule
|
||||
will have the highest priority. Example:
|
||||
|
||||
.nf
|
||||
UPGRADE ^etc/.*$ NO
|
||||
UPGRADE ^var/log/.*$ NO
|
||||
UPGRADE ^etc/X11/.*$ YES
|
||||
UPGRADE ^etc/X11/XF86Config$ NO
|
||||
.fi
|
||||
|
||||
The above example will cause pkgadd to never upgrade anything in /etc/ or /var/log/ (subdirectories included),
|
||||
except files in /etc/X11/ (subdirectories included), unless it is the file /etc/X11/XF86Config.
|
||||
The default rule is to upgrade everything, rules in this file are exceptions to that rule.
|
||||
(NOTE! A \fIpattern\fP should never contain an initial "/" since you are referring to the files in the
|
||||
package, not the files on the disk.)
|
||||
|
||||
If pkgadd finds that a specific file should not be upgraded it will install it under \fI/var/lib/pkg/rejected/\fP.
|
||||
The user is then free to examine/use/remove that file manually.
|
||||
.SH FILES
|
||||
.TP
|
||||
.B "/etc/pkgadd.conf"
|
||||
Configuration file.
|
||||
.SH SEE ALSO
|
||||
pkgrm(8), pkginfo(8), pkgmk(8), rejmerge(8)
|
||||
.SH COPYRIGHT
|
||||
pkgadd (pkgutils) is Copyright (c) 2000-2005 Per Liden and is licensed through
|
||||
the GNU General Public License. Read the COPYING file for the complete license.
|
206
pkgadd.cc
Normal file
206
pkgadd.cc
Normal file
@ -0,0 +1,206 @@
|
||||
//
|
||||
// pkgutils
|
||||
//
|
||||
// Copyright (c) 2000-2005 Per Liden
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
// USA.
|
||||
//
|
||||
|
||||
#include "pkgadd.h"
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <cstdio>
|
||||
#include <regex.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void pkgadd::run(int argc, char** argv)
|
||||
{
|
||||
//
|
||||
// Check command line options
|
||||
//
|
||||
string o_root;
|
||||
string o_package;
|
||||
bool o_upgrade = false;
|
||||
bool o_force = false;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
string option(argv[i]);
|
||||
if (option == "-r" || option == "--root") {
|
||||
assert_argument(argv, argc, i);
|
||||
o_root = argv[i + 1];
|
||||
i++;
|
||||
} else if (option == "-u" || option == "--upgrade") {
|
||||
o_upgrade = true;
|
||||
} else if (option == "-f" || option == "--force") {
|
||||
o_force = true;
|
||||
} else if (option[0] == '-' || !o_package.empty()) {
|
||||
throw runtime_error("invalid option " + option);
|
||||
} else {
|
||||
o_package = option;
|
||||
}
|
||||
}
|
||||
|
||||
if (o_package.empty())
|
||||
throw runtime_error("option missing");
|
||||
|
||||
//
|
||||
// Check UID
|
||||
//
|
||||
if (getuid())
|
||||
throw runtime_error("only root can install/upgrade packages");
|
||||
|
||||
//
|
||||
// Install/upgrade package
|
||||
//
|
||||
{
|
||||
db_lock lock(o_root, true);
|
||||
db_open(o_root);
|
||||
|
||||
pair<string, pkginfo_t> package = pkg_open(o_package);
|
||||
vector<rule_t> config_rules = read_config();
|
||||
|
||||
bool installed = db_find_pkg(package.first);
|
||||
if (installed && !o_upgrade)
|
||||
throw runtime_error("package " + package.first + " already installed (use -u to upgrade)");
|
||||
else if (!installed && o_upgrade)
|
||||
throw runtime_error("package " + package.first + " not previously installed (skip -u to install)");
|
||||
|
||||
set<string> conflicting_files = db_find_conflicts(package.first, package.second);
|
||||
|
||||
if (!conflicting_files.empty()) {
|
||||
if (o_force) {
|
||||
set<string> keep_list;
|
||||
if (o_upgrade) // Don't remove files matching the rules in configuration
|
||||
keep_list = make_keep_list(conflicting_files, config_rules);
|
||||
db_rm_files(conflicting_files, keep_list); // Remove unwanted conflicts
|
||||
} else {
|
||||
copy(conflicting_files.begin(), conflicting_files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
throw runtime_error("listed file(s) already installed (use -f to ignore and overwrite)");
|
||||
}
|
||||
}
|
||||
|
||||
set<string> keep_list;
|
||||
|
||||
if (o_upgrade) {
|
||||
keep_list = make_keep_list(package.second.files, config_rules);
|
||||
db_rm_pkg(package.first, keep_list);
|
||||
}
|
||||
|
||||
db_add_pkg(package.first, package.second);
|
||||
db_commit();
|
||||
pkg_install(o_package, keep_list);
|
||||
ldconfig();
|
||||
}
|
||||
}
|
||||
|
||||
void pkgadd::print_help() const
|
||||
{
|
||||
cout << "usage: " << utilname << " [options] <file>" << endl
|
||||
<< "options:" << endl
|
||||
<< " -u, --upgrade upgrade package with the same name" << endl
|
||||
<< " -f, --force force install, overwrite conflicting files" << endl
|
||||
<< " -r, --root <path> specify alternative installation root" << endl
|
||||
<< " -v, --version print version and exit" << endl
|
||||
<< " -h, --help print help and exit" << endl;
|
||||
}
|
||||
|
||||
vector<rule_t> pkgadd::read_config() const
|
||||
{
|
||||
vector<rule_t> rules;
|
||||
unsigned int linecount = 0;
|
||||
const string filename = root + PKGADD_CONF;
|
||||
ifstream in(filename.c_str());
|
||||
|
||||
if (in) {
|
||||
while (!in.eof()) {
|
||||
string line;
|
||||
getline(in, line);
|
||||
linecount++;
|
||||
if (!line.empty() && line[0] != '#') {
|
||||
if (line.length() >= PKGADD_CONF_MAXLINE)
|
||||
throw runtime_error(filename + ":" + itos(linecount) + ": line too long, aborting");
|
||||
|
||||
char event[PKGADD_CONF_MAXLINE];
|
||||
char pattern[PKGADD_CONF_MAXLINE];
|
||||
char action[PKGADD_CONF_MAXLINE];
|
||||
char dummy[PKGADD_CONF_MAXLINE];
|
||||
if (sscanf(line.c_str(), "%s %s %s %s", event, pattern, action, dummy) != 3)
|
||||
throw runtime_error(filename + ":" + itos(linecount) + ": wrong number of arguments, aborting");
|
||||
|
||||
if (!strcmp(event, "UPGRADE")) {
|
||||
rule_t rule;
|
||||
rule.event = rule_t::UPGRADE;
|
||||
rule.pattern = pattern;
|
||||
if (!strcmp(action, "YES")) {
|
||||
rule.action = true;
|
||||
} else if (!strcmp(action, "NO")) {
|
||||
rule.action = false;
|
||||
} else
|
||||
throw runtime_error(filename + ":" + itos(linecount) + ": '" +
|
||||
string(action) + "' unknown action, should be YES or NO, aborting");
|
||||
|
||||
rules.push_back(rule);
|
||||
} else
|
||||
throw runtime_error(filename + ":" + itos(linecount) + ": '" +
|
||||
string(event) + "' unknown event, aborting");
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Configuration:" << endl;
|
||||
for (vector<rule_t>::const_iterator j = rules.begin(); j != rules.end(); j++) {
|
||||
cerr << "\t" << (*j).pattern << "\t" << (*j).action << endl;
|
||||
}
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
set<string> pkgadd::make_keep_list(const set<string>& files, const vector<rule_t>& rules) const
|
||||
{
|
||||
set<string> keep_list;
|
||||
|
||||
for (set<string>::const_iterator i = files.begin(); i != files.end(); i++) {
|
||||
for (vector<rule_t>::const_reverse_iterator j = rules.rbegin(); j != rules.rend(); j++) {
|
||||
if ((*j).event == rule_t::UPGRADE) {
|
||||
regex_t preg;
|
||||
if (regcomp(&preg, (*j).pattern.c_str(), REG_EXTENDED | REG_NOSUB))
|
||||
throw runtime_error("error compiling regular expression '" + (*j).pattern + "', aborting");
|
||||
|
||||
if (!regexec(&preg, (*i).c_str(), 0, 0, 0)) {
|
||||
if (!(*j).action)
|
||||
keep_list.insert(keep_list.end(), *i);
|
||||
regfree(&preg);
|
||||
break;
|
||||
}
|
||||
regfree(&preg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Keep list:" << endl;
|
||||
for (set<string>::const_iterator j = keep_list.begin(); j != keep_list.end(); j++) {
|
||||
cerr << " " << (*j) << endl;
|
||||
}
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
return keep_list;
|
||||
}
|
21
pkgadd.conf
Normal file
21
pkgadd.conf
Normal file
@ -0,0 +1,21 @@
|
||||
#
|
||||
# /etc/pkgadd.conf: pkgadd(8) configuration
|
||||
#
|
||||
|
||||
# Default rule (implicit)
|
||||
#UPGRADE ^.*$ YES
|
||||
|
||||
UPGRADE ^etc/.*$ NO
|
||||
UPGRADE ^var/log/.*$ NO
|
||||
|
||||
UPGRADE ^etc/mail/cf/.*$ YES
|
||||
UPGRADE ^etc/ports/drivers/.*$ YES
|
||||
UPGRADE ^etc/X11/.*$ YES
|
||||
|
||||
UPGRADE ^etc/rc.*$ YES
|
||||
UPGRADE ^etc/rc\.local$ NO
|
||||
UPGRADE ^etc/rc\.modules$ NO
|
||||
UPGRADE ^etc/rc\.conf$ NO
|
||||
UPGRADE ^etc/rc\.d/net$ NO
|
||||
|
||||
# End of file
|
49
pkgadd.h
Normal file
49
pkgadd.h
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// pkgutils
|
||||
//
|
||||
// Copyright (c) 2000-2005 Per Liden
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
// USA.
|
||||
//
|
||||
|
||||
#ifndef PKGADD_H
|
||||
#define PKGADD_H
|
||||
|
||||
#include "pkgutil.h"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#define PKGADD_CONF "/etc/pkgadd.conf"
|
||||
#define PKGADD_CONF_MAXLINE 1024
|
||||
|
||||
struct rule_t {
|
||||
enum { UPGRADE } event;
|
||||
string pattern;
|
||||
bool action;
|
||||
};
|
||||
|
||||
class pkgadd : public pkgutil {
|
||||
public:
|
||||
pkgadd() : pkgutil("pkgadd") {}
|
||||
virtual void run(int argc, char** argv);
|
||||
virtual void print_help() const;
|
||||
|
||||
private:
|
||||
vector<rule_t> read_config() const;
|
||||
set<string> make_keep_list(const set<string>& files, const vector<rule_t>& rules) const;
|
||||
};
|
||||
|
||||
#endif /* PKGADD_H */
|
41
pkginfo.8.in
Normal file
41
pkginfo.8.in
Normal file
@ -0,0 +1,41 @@
|
||||
.TH pkginfo 8 "" "pkgutils #VERSION#" ""
|
||||
.SH NAME
|
||||
pkginfo \- display software package information
|
||||
.SH SYNOPSIS
|
||||
\fBpkginfo [options]\fP
|
||||
.SH DESCRIPTION
|
||||
\fBpkginfo\fP is a \fIpackage management\fP utility, which displays
|
||||
information about software packages that are installed on the system
|
||||
or that reside in a particular directory.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B "\-i, \-\-installed"
|
||||
List installed packages and their version.
|
||||
.TP
|
||||
.B "\-l, \-\-list <package|file>"
|
||||
List files owned by the specified <package> or contained in <file>.
|
||||
.TP
|
||||
.B "\-o, \-\-owner <pattern>"
|
||||
List owner(s) of file(s) matching <pattern>.
|
||||
.TP
|
||||
.B "\-f, \-\-footprint <file>"
|
||||
Print footprint for <file>. This feature is mainly used by pkgmk(8)
|
||||
for creating and comparing footprints.
|
||||
.TP
|
||||
.B "\-r, \-\-root <path>"
|
||||
Specify alternative installation root (default is "/"). This
|
||||
should be used if you want to display information about a package
|
||||
that is installed on a temporary mounted partition, which is "owned"
|
||||
by another system. By using this option you specify which package
|
||||
database to use.
|
||||
.TP
|
||||
.B "\-v, \-\-version"
|
||||
Print version and exit.
|
||||
.TP
|
||||
.B "\-h, \-\-help"
|
||||
Print help and exit.
|
||||
.SH SEE ALSO
|
||||
pkgadd(8), pkgrm(8), pkgmk(8), rejmerge(8)
|
||||
.SH COPYRIGHT
|
||||
pkginfo (pkgutils) is Copyright (c) 2000-2005 Per Liden and is licensed through
|
||||
the GNU General Public License. Read the COPYING file for the complete license.
|
154
pkginfo.cc
Normal file
154
pkginfo.cc
Normal file
@ -0,0 +1,154 @@
|
||||
//
|
||||
// pkgutils
|
||||
//
|
||||
// Copyright (c) 2000-2005 Per Liden
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
// USA.
|
||||
//
|
||||
|
||||
#include "pkginfo.h"
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
#include <iomanip>
|
||||
#include <sys/types.h>
|
||||
#include <regex.h>
|
||||
|
||||
void pkginfo::run(int argc, char** argv)
|
||||
{
|
||||
//
|
||||
// Check command line options
|
||||
//
|
||||
int o_footprint_mode = 0;
|
||||
int o_installed_mode = 0;
|
||||
int o_list_mode = 0;
|
||||
int o_owner_mode = 0;
|
||||
string o_root;
|
||||
string o_arg;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
string option(argv[i]);
|
||||
if (option == "-r" || option == "--root") {
|
||||
assert_argument(argv, argc, i);
|
||||
o_root = argv[i + 1];
|
||||
i++;
|
||||
} else if (option == "-i" || option == "--installed") {
|
||||
o_installed_mode += 1;
|
||||
} else if (option == "-l" || option == "--list") {
|
||||
assert_argument(argv, argc, i);
|
||||
o_list_mode += 1;
|
||||
o_arg = argv[i + 1];
|
||||
i++;
|
||||
} else if (option == "-o" || option == "--owner") {
|
||||
assert_argument(argv, argc, i);
|
||||
o_owner_mode += 1;
|
||||
o_arg = argv[i + 1];
|
||||
i++;
|
||||
} else if (option == "-f" || option == "--footprint") {
|
||||
assert_argument(argv, argc, i);
|
||||
o_footprint_mode += 1;
|
||||
o_arg = argv[i + 1];
|
||||
i++;
|
||||
} else {
|
||||
throw runtime_error("invalid option " + option);
|
||||
}
|
||||
}
|
||||
|
||||
if (o_footprint_mode + o_installed_mode + o_list_mode + o_owner_mode == 0)
|
||||
throw runtime_error("option missing");
|
||||
|
||||
if (o_footprint_mode + o_installed_mode + o_list_mode + o_owner_mode > 1)
|
||||
throw runtime_error("too many options");
|
||||
|
||||
if (o_footprint_mode) {
|
||||
//
|
||||
// Make footprint
|
||||
//
|
||||
pkg_footprint(o_arg);
|
||||
} else {
|
||||
//
|
||||
// Modes that require the database to be opened
|
||||
//
|
||||
{
|
||||
db_lock lock(o_root, false);
|
||||
db_open(o_root);
|
||||
}
|
||||
|
||||
if (o_installed_mode) {
|
||||
//
|
||||
// List installed packages
|
||||
//
|
||||
for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i)
|
||||
cout << i->first << ' ' << i->second.version << endl;
|
||||
} else if (o_list_mode) {
|
||||
//
|
||||
// List package or file contents
|
||||
//
|
||||
if (db_find_pkg(o_arg)) {
|
||||
copy(packages[o_arg].files.begin(), packages[o_arg].files.end(), ostream_iterator<string>(cout, "\n"));
|
||||
} else if (file_exists(o_arg)) {
|
||||
pair<string, pkginfo_t> package = pkg_open(o_arg);
|
||||
copy(package.second.files.begin(), package.second.files.end(), ostream_iterator<string>(cout, "\n"));
|
||||
} else {
|
||||
throw runtime_error(o_arg + " is neither an installed package nor a package file");
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// List owner(s) of file or directory
|
||||
//
|
||||
regex_t preg;
|
||||
if (regcomp(&preg, o_arg.c_str(), REG_EXTENDED | REG_NOSUB))
|
||||
throw runtime_error("error compiling regular expression '" + o_arg + "', aborting");
|
||||
|
||||
vector<pair<string, string> > result;
|
||||
result.push_back(pair<string, string>("Package", "File"));
|
||||
unsigned int width = result.begin()->first.length(); // Width of "Package"
|
||||
|
||||
for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i) {
|
||||
for (set<string>::const_iterator j = i->second.files.begin(); j != i->second.files.end(); ++j) {
|
||||
const string file('/' + *j);
|
||||
if (!regexec(&preg, file.c_str(), 0, 0, 0)) {
|
||||
result.push_back(pair<string, string>(i->first, *j));
|
||||
if (i->first.length() > width)
|
||||
width = i->first.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
regfree(&preg);
|
||||
|
||||
if (result.size() > 1) {
|
||||
for (vector<pair<string, string> >::const_iterator i = result.begin(); i != result.end(); ++i) {
|
||||
cout << left << setw(width + 2) << i->first << i->second << endl;
|
||||
}
|
||||
} else {
|
||||
cout << utilname << ": no owner(s) found" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pkginfo::print_help() const
|
||||
{
|
||||
cout << "usage: " << utilname << " [options]" << endl
|
||||
<< "options:" << endl
|
||||
<< " -i, --installed list installed packages" << endl
|
||||
<< " -l, --list <package|file> list files in <package> or <file>" << endl
|
||||
<< " -o, --owner <pattern> list owner(s) of file(s) matching <pattern>" << endl
|
||||
<< " -f, --footprint <file> print footprint for <file>" << endl
|
||||
<< " -r, --root <path> specify alternative installation root" << endl
|
||||
<< " -v, --version print version and exit" << endl
|
||||
<< " -h, --help print help and exit" << endl;
|
||||
}
|
34
pkginfo.h
Normal file
34
pkginfo.h
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// pkgutils
|
||||
//
|
||||
// Copyright (c) 2000-2005 Per Liden
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
// USA.
|
||||
//
|
||||
|
||||
#ifndef PKGINFO_H
|
||||
#define PKGINFO_H
|
||||
|
||||
#include "pkgutil.h"
|
||||
|
||||
class pkginfo : public pkgutil {
|
||||
public:
|
||||
pkginfo() : pkgutil("pkginfo") {}
|
||||
virtual void run(int argc, char** argv);
|
||||
virtual void print_help() const;
|
||||
};
|
||||
|
||||
#endif /* PKGINFO_H */
|
92
pkgmk.8.in
Normal file
92
pkgmk.8.in
Normal file
@ -0,0 +1,92 @@
|
||||
.TH pkgmk 8 "" "pkgutils #VERSION#" ""
|
||||
.SH NAME
|
||||
pkgmk \- make software package
|
||||
.SH SYNOPSIS
|
||||
\fBpkgmk [options]\fP
|
||||
.SH DESCRIPTION
|
||||
\fBpkgmk\fP is a \fIpackage management\fP utility, which makes
|
||||
a software package. A \fIpackage\fP is an archive of files (.pkg.tar.gz)
|
||||
that can be installed using pkgadd(8).
|
||||
|
||||
To prepare to use pkgmk, you must write a file named \fIPkgfile\fP
|
||||
that describes how the package should be build. Once a suitable
|
||||
\fIPkgfile\fP file exists, each time you change some source files,
|
||||
you simply execute pkgmk to bring the package up to date. The pkgmk
|
||||
program uses the \fIPkgfile\fP file and the last-modification
|
||||
times of the source files to decide if the package needs to be updated.
|
||||
|
||||
Global build configuration is stored in \fI/etc/pkgmk.conf\fP. This
|
||||
file is read by pkgmk at startup.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B "\-i, \-\-install"
|
||||
Install package using pkgadd(8) after successful build.
|
||||
.TP
|
||||
.B "\-u, \-\-upgrade"
|
||||
Install package as an upgrade using pkgadd(8) after successful build.
|
||||
.TP
|
||||
.B "\-r, \-\-recursive"
|
||||
Search for and build packages recursively.
|
||||
.TP
|
||||
.B "\-d, \-\-download"
|
||||
Download missing source file(s).
|
||||
.TP
|
||||
.B "\-do, \-\-download\-only"
|
||||
Do not build, only download missing source file(s).
|
||||
.TP
|
||||
.B "\-utd, \-\-up\-to\-date"
|
||||
Do not build, only check if the package is up to date.
|
||||
.TP
|
||||
.B "\-uf, \-\-update\-footprint"
|
||||
Update footprint and treat last build as successful.
|
||||
.TP
|
||||
.B "\-if, \-\-ignore\-footprint"
|
||||
Build package without checking footprint.
|
||||
.TP
|
||||
.B "\-um, \-\-update\-md5sum"
|
||||
Update md5sum using the current source files.
|
||||
.TP
|
||||
.B "\-im, \-\-ignore\-md5sum"
|
||||
Build package without checking md5sum first.
|
||||
.TP
|
||||
.B "\-ns, \-\-no\-strip"
|
||||
Do not strip executable binaries or libraries.
|
||||
.TP
|
||||
.B "\-f, \-\-force"
|
||||
Build package even if it appears to be up to date.
|
||||
.TP
|
||||
.B "\-c, \-\-clean"
|
||||
Remove the (previously built) package and the downloaded source files.
|
||||
.TP
|
||||
.B "\-kw, \-\-keep-work"
|
||||
Keep temporary working directory.
|
||||
.TP
|
||||
.B "\-cf, \-\-config\-file <file>"
|
||||
Use alternative configuration file (default is /etc/pkgmk.conf).
|
||||
.TP
|
||||
.B "\-v, \-\-version"
|
||||
Print version and exit.
|
||||
.TP
|
||||
.B "\-h, \-\-help"
|
||||
Print help and exit.
|
||||
.SH FILES
|
||||
.TP
|
||||
.B "Pkgfile"
|
||||
Package build description.
|
||||
.TP
|
||||
.B ".footprint"
|
||||
Package footprint (used for regression testing).
|
||||
.TP
|
||||
.B ".md5sum"
|
||||
MD5 checksum of source files.
|
||||
.TP
|
||||
.B "/etc/pkgmk.conf"
|
||||
Global package make configuration.
|
||||
.TP
|
||||
.B "wget"
|
||||
Used by pkgmk to download source code.
|
||||
.SH SEE ALSO
|
||||
pkgadd(8), pkgrm(8), pkginfo(8), rejmerge(8), wget(1)
|
||||
.SH COPYRIGHT
|
||||
pkgmk (pkgutils) is Copyright (c) 2000-2005 Per Liden and is licensed through
|
||||
the GNU General Public License. Read the COPYING file for the complete license.
|
15
pkgmk.conf
Normal file
15
pkgmk.conf
Normal file
@ -0,0 +1,15 @@
|
||||
#
|
||||
# /etc/pkgmk.conf: pkgmk(8) configuration
|
||||
#
|
||||
|
||||
export CFLAGS="-O2 -march=i686 -pipe"
|
||||
export CXXFLAGS="-O2 -march=i686 -pipe"
|
||||
|
||||
# PKGMK_SOURCE_DIR="$PWD"
|
||||
# PKGMK_PACKAGE_DIR="$PWD"
|
||||
# PKGMK_WORK_DIR="$PWD/work"
|
||||
# PKGMK_DOWNLOAD="no"
|
||||
# PKGMK_IGNORE_FOOTPRINT="no"
|
||||
# PKGMK_NO_STRIP="no"
|
||||
|
||||
# End of file
|
653
pkgmk.in
Executable file
653
pkgmk.in
Executable file
@ -0,0 +1,653 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# pkgutils
|
||||
#
|
||||
# Copyright (c) 2000-2005 Per Liden
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
# USA.
|
||||
#
|
||||
|
||||
info() {
|
||||
echo "=======> $1"
|
||||
}
|
||||
|
||||
warning() {
|
||||
info "WARNING: $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
info "ERROR: $1"
|
||||
}
|
||||
|
||||
get_filename() {
|
||||
local FILE="`echo $1 | sed 's|^.*://.*/||g'`"
|
||||
|
||||
if [ "$FILE" != "$1" ]; then
|
||||
FILE="$PKGMK_SOURCE_DIR/$FILE"
|
||||
fi
|
||||
|
||||
echo $FILE
|
||||
}
|
||||
|
||||
check_pkgfile() {
|
||||
if [ ! "$name" ]; then
|
||||
error "Variable 'name' not specified in $PKGMK_PKGFILE."
|
||||
exit 1
|
||||
elif [ ! "$version" ]; then
|
||||
error "Variable 'version' not specified in $PKGMK_PKGFILE."
|
||||
exit 1
|
||||
elif [ ! "$release" ]; then
|
||||
error "Variable 'release' not specified in $PKGMK_PKGFILE."
|
||||
exit 1
|
||||
elif [ "`type -t build`" != "function" ]; then
|
||||
error "Function 'build' not specified in $PKGMK_PKGFILE."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_directory() {
|
||||
if [ ! -d $1 ]; then
|
||||
error "Directory '$1' does not exist."
|
||||
exit 1
|
||||
elif [ ! -w $1 ]; then
|
||||
error "Directory '$1' not writable."
|
||||
exit 1
|
||||
elif [ ! -x $1 ] || [ ! -r $1 ]; then
|
||||
error "Directory '$1' not readable."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
download_file() {
|
||||
info "Downloading '$1'."
|
||||
|
||||
if [ ! "`type -p wget`" ]; then
|
||||
error "Command 'wget' not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LOCAL_FILENAME=`get_filename $1`
|
||||
LOCAL_FILENAME_PARTIAL="$LOCAL_FILENAME.partial"
|
||||
DOWNLOAD_CMD="--passive-ftp --no-directories --tries=3 --waitretry=3 \
|
||||
--directory-prefix=$PKGMK_SOURCE_DIR --output-document=$LOCAL_FILENAME_PARTIAL $1"
|
||||
|
||||
if [ -f "$LOCAL_FILENAME_PARTIAL" ]; then
|
||||
info "Partial download found, trying to resume"
|
||||
RESUME_CMD="-c"
|
||||
fi
|
||||
|
||||
while true; do
|
||||
wget $RESUME_CMD $DOWNLOAD_CMD
|
||||
error=$?
|
||||
if [ $error != 0 ] && [ "$RESUME_CMD" ]; then
|
||||
info "Partial download failed, restarting"
|
||||
rm -f "$LOCAL_FILENAME_PARTIAL"
|
||||
RESUME_CMD=""
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $error != 0 ]; then
|
||||
error "Downloading '$1' failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mv -f "$LOCAL_FILENAME_PARTIAL" "$LOCAL_FILENAME"
|
||||
}
|
||||
|
||||
download_source() {
|
||||
local FILE LOCAL_FILENAME
|
||||
|
||||
for FILE in ${source[@]}; do
|
||||
LOCAL_FILENAME=`get_filename $FILE`
|
||||
if [ ! -e $LOCAL_FILENAME ]; then
|
||||
if [ "$LOCAL_FILENAME" = "$FILE" ]; then
|
||||
error "Source file '$LOCAL_FILENAME' not found (can not be downloaded, URL not specified)."
|
||||
exit 1
|
||||
else
|
||||
if [ "$PKGMK_DOWNLOAD" = "yes" ]; then
|
||||
download_file $FILE
|
||||
else
|
||||
error "Source file '$LOCAL_FILENAME' not found (use option -d to download)."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
unpack_source() {
|
||||
local FILE LOCAL_FILENAME COMMAND
|
||||
|
||||
for FILE in ${source[@]}; do
|
||||
LOCAL_FILENAME=`get_filename $FILE`
|
||||
case $LOCAL_FILENAME in
|
||||
*.tar.gz|*.tar.Z|*.tgz)
|
||||
COMMAND="tar -C $SRC --use-compress-program=gzip -xf $LOCAL_FILENAME" ;;
|
||||
*.tar.bz2)
|
||||
COMMAND="tar -C $SRC --use-compress-program=bzip2 -xf $LOCAL_FILENAME" ;;
|
||||
*.zip)
|
||||
COMMAND="unzip -qq -o -d $SRC $LOCAL_FILENAME" ;;
|
||||
*)
|
||||
COMMAND="cp $LOCAL_FILENAME $SRC" ;;
|
||||
esac
|
||||
|
||||
echo "$COMMAND"
|
||||
|
||||
$COMMAND
|
||||
|
||||
if [ $? != 0 ]; then
|
||||
if [ "$PKGMK_KEEP_WORK" = "no" ]; then
|
||||
rm -rf $PKGMK_WORK_DIR
|
||||
fi
|
||||
error "Building '$TARGET' failed."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
make_md5sum() {
|
||||
local FILE LOCAL_FILENAMES
|
||||
|
||||
if [ "$source" ]; then
|
||||
for FILE in ${source[@]}; do
|
||||
LOCAL_FILENAMES="$LOCAL_FILENAMES `get_filename $FILE`"
|
||||
done
|
||||
|
||||
md5sum $LOCAL_FILENAMES | sed -e 's| .*/| |' | sort -k 2
|
||||
fi
|
||||
}
|
||||
|
||||
make_footprint() {
|
||||
pkginfo --footprint $TARGET | \
|
||||
sed "s|\tlib/modules/`uname -r`/|\tlib/modules/<kernel-version>/|g" | \
|
||||
sort -k 3
|
||||
}
|
||||
|
||||
check_md5sum() {
|
||||
local FILE="$PKGMK_WORK_DIR/.tmp"
|
||||
|
||||
cd $PKGMK_ROOT
|
||||
|
||||
if [ -f $PKGMK_MD5SUM ]; then
|
||||
make_md5sum > $FILE.md5sum
|
||||
sort -k 2 $PKGMK_MD5SUM > $FILE.md5sum.orig
|
||||
diff -w -t -U 0 $FILE.md5sum.orig $FILE.md5sum | \
|
||||
sed '/^@@/d' | \
|
||||
sed '/^+++/d' | \
|
||||
sed '/^---/d' | \
|
||||
sed 's/^+/NEW /g' | \
|
||||
sed 's/^-/MISSING /g' > $FILE.md5sum.diff
|
||||
if [ -s $FILE.md5sum.diff ]; then
|
||||
error "Md5sum mismatch found:"
|
||||
cat $FILE.md5sum.diff
|
||||
|
||||
if [ "$PKGMK_KEEP_WORK" = "no" ]; then
|
||||
rm -rf $PKGMK_WORK_DIR
|
||||
fi
|
||||
|
||||
if [ "$PKGMK_CHECK_MD5SUM" = "yes" ]; then
|
||||
error "Md5sum not ok."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
error "Building '$TARGET' failed."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [ "$PKGMK_CHECK_MD5SUM" = "yes" ]; then
|
||||
if [ "$PKGMK_KEEP_WORK" = "no" ]; then
|
||||
rm -rf $PKGMK_WORK_DIR
|
||||
fi
|
||||
info "Md5sum not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
warning "Md5sum not found, creating new."
|
||||
make_md5sum > $PKGMK_MD5SUM
|
||||
fi
|
||||
|
||||
if [ "$PKGMK_CHECK_MD5SUM" = "yes" ]; then
|
||||
if [ "$PKGMK_KEEP_WORK" = "no" ]; then
|
||||
rm -rf $PKGMK_WORK_DIR
|
||||
fi
|
||||
info "Md5sum ok."
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
strip_files() {
|
||||
local FILE FILTER
|
||||
|
||||
cd $PKG
|
||||
|
||||
if [ -f $PKGMK_ROOT/$PKGMK_NOSTRIP ]; then
|
||||
FILTER="grep -v -f $PKGMK_ROOT/$PKGMK_NOSTRIP"
|
||||
else
|
||||
FILTER="cat"
|
||||
fi
|
||||
|
||||
find . -type f -printf "%P\n" | $FILTER | while read FILE; do
|
||||
if file -b "$FILE" | grep '^.*ELF.*executable.*not stripped$' &> /dev/null; then
|
||||
strip --strip-all "$FILE"
|
||||
elif file -b "$FILE" | grep '^.*ELF.*shared object.*not stripped$' &> /dev/null; then
|
||||
strip --strip-unneeded "$FILE"
|
||||
elif file -b "$FILE" | grep '^current ar archive$' &> /dev/null; then
|
||||
strip --strip-debug "$FILE"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
compress_manpages() {
|
||||
local FILE DIR TARGET
|
||||
|
||||
cd $PKG
|
||||
|
||||
find . -type f -path "*/man/man*/*" | while read FILE; do
|
||||
if [ "$FILE" = "${FILE%%.gz}" ]; then
|
||||
gzip -9 "$FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
find . -type l -path "*/man/man*/*" | while read FILE; do
|
||||
TARGET=`readlink -n "$FILE"`
|
||||
TARGET="${TARGET##*/}"
|
||||
TARGET="${TARGET%%.gz}.gz"
|
||||
rm -f "$FILE"
|
||||
FILE="${FILE%%.gz}.gz"
|
||||
DIR=`dirname "$FILE"`
|
||||
|
||||
if [ -e "$DIR/$TARGET" ]; then
|
||||
ln -sf "$TARGET" "$FILE"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_footprint() {
|
||||
local FILE="$PKGMK_WORK_DIR/.tmp"
|
||||
|
||||
cd $PKGMK_ROOT
|
||||
|
||||
if [ -f $TARGET ]; then
|
||||
make_footprint > $FILE.footprint
|
||||
if [ -f $PKGMK_FOOTPRINT ]; then
|
||||
sort -k 3 $PKGMK_FOOTPRINT > $FILE.footprint.orig
|
||||
diff -w -t -U 0 $FILE.footprint.orig $FILE.footprint | \
|
||||
sed '/^@@/d' | \
|
||||
sed '/^+++/d' | \
|
||||
sed '/^---/d' | \
|
||||
sed 's/^+/NEW /g' | \
|
||||
sed 's/^-/MISSING /g' > $FILE.footprint.diff
|
||||
if [ -s $FILE.footprint.diff ]; then
|
||||
error "Footprint mismatch found:"
|
||||
cat $FILE.footprint.diff
|
||||
BUILD_SUCCESSFUL="no"
|
||||
fi
|
||||
else
|
||||
warning "Footprint not found, creating new."
|
||||
mv $FILE.footprint $PKGMK_FOOTPRINT
|
||||
fi
|
||||
else
|
||||
error "Package '$TARGET' was not found."
|
||||
BUILD_SUCCESSFUL="no"
|
||||
fi
|
||||
}
|
||||
|
||||
build_package() {
|
||||
local BUILD_SUCCESSFUL="no"
|
||||
|
||||
export PKG="$PKGMK_WORK_DIR/pkg"
|
||||
export SRC="$PKGMK_WORK_DIR/src"
|
||||
umask 022
|
||||
|
||||
cd $PKGMK_ROOT
|
||||
rm -rf $PKGMK_WORK_DIR
|
||||
mkdir -p $SRC $PKG
|
||||
|
||||
if [ "$PKGMK_IGNORE_MD5SUM" = "no" ]; then
|
||||
check_md5sum
|
||||
fi
|
||||
|
||||
if [ "$UID" != "0" ]; then
|
||||
warning "Packages should be built as root."
|
||||
fi
|
||||
|
||||
info "Building '$TARGET'."
|
||||
|
||||
unpack_source
|
||||
|
||||
cd $SRC
|
||||
(set -e -x ; build)
|
||||
|
||||
if [ $? = 0 ]; then
|
||||
if [ "$PKGMK_NO_STRIP" = "no" ]; then
|
||||
strip_files
|
||||
fi
|
||||
|
||||
compress_manpages
|
||||
|
||||
cd $PKG
|
||||
info "Build result:"
|
||||
tar czvvf $TARGET *
|
||||
|
||||
if [ $? = 0 ]; then
|
||||
BUILD_SUCCESSFUL="yes"
|
||||
|
||||
if [ "$PKGMK_IGNORE_FOOTPRINT" = "yes" ]; then
|
||||
warning "Footprint ignored."
|
||||
else
|
||||
check_footprint
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$PKGMK_KEEP_WORK" = "no" ]; then
|
||||
rm -rf $PKGMK_WORK_DIR
|
||||
fi
|
||||
|
||||
if [ "$BUILD_SUCCESSFUL" = "yes" ]; then
|
||||
info "Building '$TARGET' succeeded."
|
||||
else
|
||||
if [ -f $TARGET ]; then
|
||||
touch -r $PKGMK_ROOT/$PKGMK_PKGFILE $TARGET &> /dev/null
|
||||
fi
|
||||
error "Building '$TARGET' failed."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_package() {
|
||||
local COMMAND
|
||||
|
||||
info "Installing '$TARGET'."
|
||||
|
||||
if [ "$PKGMK_INSTALL" = "install" ]; then
|
||||
COMMAND="pkgadd $TARGET"
|
||||
else
|
||||
COMMAND="pkgadd -u $TARGET"
|
||||
fi
|
||||
|
||||
cd $PKGMK_ROOT
|
||||
echo "$COMMAND"
|
||||
$COMMAND
|
||||
|
||||
if [ $? = 0 ]; then
|
||||
info "Installing '$TARGET' succeeded."
|
||||
else
|
||||
error "Installing '$TARGET' failed."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
recursive() {
|
||||
local ARGS FILE DIR
|
||||
|
||||
ARGS=`echo "$@" | sed -e "s/--recursive//g" -e "s/-r//g"`
|
||||
|
||||
for FILE in `find $PKGMK_ROOT -name $PKGMK_PKGFILE | sort`; do
|
||||
DIR="`dirname $FILE`/"
|
||||
if [ -d $DIR ]; then
|
||||
info "Entering directory '$DIR'."
|
||||
(cd $DIR && $PKGMK_COMMAND $ARGS)
|
||||
info "Leaving directory '$DIR'."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
clean() {
|
||||
local FILE LOCAL_FILENAME
|
||||
|
||||
if [ -f $TARGET ]; then
|
||||
info "Removing $TARGET"
|
||||
rm -f $TARGET
|
||||
fi
|
||||
|
||||
for FILE in ${source[@]}; do
|
||||
LOCAL_FILENAME=`get_filename $FILE`
|
||||
if [ -e $LOCAL_FILENAME ] && [ "$LOCAL_FILENAME" != "$FILE" ]; then
|
||||
info "Removing $LOCAL_FILENAME"
|
||||
rm -f $LOCAL_FILENAME
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
update_footprint() {
|
||||
if [ ! -f $TARGET ]; then
|
||||
error "Unable to update footprint. File '$TARGET' not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
make_footprint > $PKGMK_FOOTPRINT
|
||||
touch $TARGET
|
||||
|
||||
info "Footprint updated."
|
||||
}
|
||||
|
||||
build_needed() {
|
||||
local FILE RESULT
|
||||
|
||||
RESULT="yes"
|
||||
if [ -f $TARGET ]; then
|
||||
RESULT="no"
|
||||
for FILE in $PKGMK_PKGFILE ${source[@]}; do
|
||||
FILE=`get_filename $FILE`
|
||||
if [ ! -e $FILE ] || [ ! $TARGET -nt $FILE ]; then
|
||||
RESULT="yes"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo $RESULT
|
||||
}
|
||||
|
||||
interrupted() {
|
||||
echo ""
|
||||
error "Interrupted."
|
||||
|
||||
if [ "$PKGMK_KEEP_WORK" = "no" ]; then
|
||||
rm -rf $PKGMK_WORK_DIR
|
||||
fi
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
print_help() {
|
||||
echo "usage: `basename $PKGMK_COMMAND` [options]"
|
||||
echo "options:"
|
||||
echo " -i, --install build and install package"
|
||||
echo " -u, --upgrade build and install package (as upgrade)"
|
||||
echo " -r, --recursive search for and build packages recursively"
|
||||
echo " -d, --download download missing source file(s)"
|
||||
echo " -do, --download-only do not build, only download missing source file(s)"
|
||||
echo " -utd, --up-to-date do not build, only check if package is up to date"
|
||||
echo " -uf, --update-footprint update footprint using result from last build"
|
||||
echo " -if, --ignore-footprint build package without checking footprint"
|
||||
echo " -um, --update-md5sum update md5sum"
|
||||
echo " -im, --ignore-md5sum build package without checking md5sum"
|
||||
echo " -cm, --check-md5sum do not build, only check md5sum"
|
||||
echo " -ns, --no-strip do not strip executable binaries or libraries"
|
||||
echo " -f, --force build package even if it appears to be up to date"
|
||||
echo " -c, --clean remove package and downloaded files"
|
||||
echo " -kw, --keep-work keep temporary working directory"
|
||||
echo " -cf, --config-file <file> use alternative configuration file"
|
||||
echo " -v, --version print version and exit "
|
||||
echo " -h, --help print help and exit"
|
||||
}
|
||||
|
||||
parse_options() {
|
||||
while [ "$1" ]; do
|
||||
case $1 in
|
||||
-i|--install)
|
||||
PKGMK_INSTALL="install" ;;
|
||||
-u|--upgrade)
|
||||
PKGMK_INSTALL="upgrade" ;;
|
||||
-r|--recursive)
|
||||
PKGMK_RECURSIVE="yes" ;;
|
||||
-d|--download)
|
||||
PKGMK_DOWNLOAD="yes" ;;
|
||||
-do|--download-only)
|
||||
PKGMK_DOWNLOAD="yes"
|
||||
PKGMK_DOWNLOAD_ONLY="yes" ;;
|
||||
-utd|--up-to-date)
|
||||
PKGMK_UP_TO_DATE="yes" ;;
|
||||
-uf|--update-footprint)
|
||||
PKGMK_UPDATE_FOOTPRINT="yes" ;;
|
||||
-if|--ignore-footprint)
|
||||
PKGMK_IGNORE_FOOTPRINT="yes" ;;
|
||||
-um|--update-md5sum)
|
||||
PKGMK_UPDATE_MD5SUM="yes" ;;
|
||||
-im|--ignore-md5sum)
|
||||
PKGMK_IGNORE_MD5SUM="yes" ;;
|
||||
-cm|--check-md5sum)
|
||||
PKGMK_CHECK_MD5SUM="yes" ;;
|
||||
-ns|--no-strip)
|
||||
PKGMK_NO_STRIP="yes" ;;
|
||||
-f|--force)
|
||||
PKGMK_FORCE="yes" ;;
|
||||
-c|--clean)
|
||||
PKGMK_CLEAN="yes" ;;
|
||||
-kw|--keep-work)
|
||||
PKGMK_KEEP_WORK="yes" ;;
|
||||
-cf|--config-file)
|
||||
if [ ! "$2" ]; then
|
||||
echo "`basename $PKGMK_COMMAND`: option $1 requires an argument"
|
||||
exit 1
|
||||
fi
|
||||
PKGMK_CONFFILE="$2"
|
||||
shift ;;
|
||||
-v|--version)
|
||||
echo "`basename $PKGMK_COMMAND` (pkgutils) $PKGMK_VERSION"
|
||||
exit 0 ;;
|
||||
-h|--help)
|
||||
print_help
|
||||
exit 0 ;;
|
||||
*)
|
||||
echo "`basename $PKGMK_COMMAND`: invalid option $1"
|
||||
exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
local FILE TARGET
|
||||
|
||||
parse_options "$@"
|
||||
|
||||
if [ "$PKGMK_RECURSIVE" = "yes" ]; then
|
||||
recursive "$@"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for FILE in $PKGMK_PKGFILE $PKGMK_CONFFILE; do
|
||||
if [ ! -f $FILE ]; then
|
||||
error "File '$FILE' not found."
|
||||
exit 1
|
||||
fi
|
||||
. $FILE
|
||||
done
|
||||
|
||||
check_directory "$PKGMK_SOURCE_DIR"
|
||||
check_directory "$PKGMK_PACKAGE_DIR"
|
||||
check_directory "`dirname $PKGMK_WORK_DIR`"
|
||||
|
||||
check_pkgfile
|
||||
|
||||
TARGET="$PKGMK_PACKAGE_DIR/$name#$version-$release.pkg.tar.gz"
|
||||
|
||||
if [ "$PKGMK_CLEAN" = "yes" ]; then
|
||||
clean
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$PKGMK_UPDATE_FOOTPRINT" = "yes" ]; then
|
||||
update_footprint
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$PKGMK_UPDATE_MD5SUM" = "yes" ]; then
|
||||
download_source
|
||||
make_md5sum > $PKGMK_MD5SUM
|
||||
info "Md5sum updated."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$PKGMK_DOWNLOAD_ONLY" = "yes" ]; then
|
||||
download_source
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$PKGMK_UP_TO_DATE" = "yes" ]; then
|
||||
if [ "`build_needed`" = "yes" ]; then
|
||||
info "Package '$TARGET' is not up to date."
|
||||
else
|
||||
info "Package '$TARGET' is up to date."
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "`build_needed`" = "no" ] && [ "$PKGMK_FORCE" = "no" ] && [ "$PKGMK_CHECK_MD5SUM" = "no" ]; then
|
||||
info "Package '$TARGET' is up to date."
|
||||
else
|
||||
download_source
|
||||
build_package
|
||||
fi
|
||||
|
||||
if [ "$PKGMK_INSTALL" != "no" ]; then
|
||||
install_package
|
||||
fi
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM
|
||||
|
||||
export LC_ALL=POSIX
|
||||
|
||||
readonly PKGMK_VERSION="#VERSION#"
|
||||
readonly PKGMK_COMMAND="$0"
|
||||
readonly PKGMK_ROOT="$PWD"
|
||||
|
||||
PKGMK_CONFFILE="/etc/pkgmk.conf"
|
||||
PKGMK_PKGFILE="Pkgfile"
|
||||
PKGMK_FOOTPRINT=".footprint"
|
||||
PKGMK_MD5SUM=".md5sum"
|
||||
PKGMK_NOSTRIP=".nostrip"
|
||||
|
||||
PKGMK_SOURCE_DIR="$PWD"
|
||||
PKGMK_PACKAGE_DIR="$PWD"
|
||||
PKGMK_WORK_DIR="$PWD/work"
|
||||
|
||||
PKGMK_INSTALL="no"
|
||||
PKGMK_RECURSIVE="no"
|
||||
PKGMK_DOWNLOAD="no"
|
||||
PKGMK_DOWNLOAD_ONLY="no"
|
||||
PKGMK_UP_TO_DATE="no"
|
||||
PKGMK_UPDATE_FOOTPRINT="no"
|
||||
PKGMK_IGNORE_FOOTPRINT="no"
|
||||
PKGMK_FORCE="no"
|
||||
PKGMK_KEEP_WORK="no"
|
||||
PKGMK_UPDATE_MD5SUM="no"
|
||||
PKGMK_IGNORE_MD5SUM="no"
|
||||
PKGMK_CHECK_MD5SUM="no"
|
||||
PKGMK_NO_STRIP="no"
|
||||
PKGMK_CLEAN="no"
|
||||
|
||||
main "$@"
|
||||
|
||||
# End of file
|
27
pkgrm.8.in
Normal file
27
pkgrm.8.in
Normal file
@ -0,0 +1,27 @@
|
||||
.TH pkgrm 8 "" "pkgutils #VERSION#" ""
|
||||
.SH NAME
|
||||
pkgrm \- remove software package
|
||||
.SH SYNOPSIS
|
||||
\fBpkgrm [options] <package>\fP
|
||||
.SH DESCRIPTION
|
||||
\fBpkgrm\fP is a \fIpackage management\fP utility, which
|
||||
removes/uninstalls a previously installed software packages.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B "\-r, \-\-root <path>"
|
||||
Specify alternative installation root (default is "/"). This
|
||||
should be used if you want to remove a package from a temporary
|
||||
mounted partition, which is "owned" by another system. By using
|
||||
this option you not only specify where the software is installed,
|
||||
but you also specify which package database to use.
|
||||
.TP
|
||||
.B "\-v, \-\-version"
|
||||
Print version and exit.
|
||||
.TP
|
||||
.B "\-h, \-\-help"
|
||||
Print help and exit.
|
||||
.SH SEE ALSO
|
||||
pkgadd(8), pkginfo(8), pkgmk(8), rejmerge(8)
|
||||
.SH COPYRIGHT
|
||||
pkgrm (pkgutils) is Copyright (c) 2000-2005 Per Liden and is licensed through
|
||||
the GNU General Public License. Read the COPYING file for the complete license.
|
78
pkgrm.cc
Normal file
78
pkgrm.cc
Normal file
@ -0,0 +1,78 @@
|
||||
//
|
||||
// pkgutils
|
||||
//
|
||||
// Copyright (c) 2000-2005 Per Liden
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
// USA.
|
||||
//
|
||||
|
||||
#include "pkgrm.h"
|
||||
#include <unistd.h>
|
||||
|
||||
void pkgrm::run(int argc, char** argv)
|
||||
{
|
||||
//
|
||||
// Check command line options
|
||||
//
|
||||
string o_package;
|
||||
string o_root;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
string option(argv[i]);
|
||||
if (option == "-r" || option == "--root") {
|
||||
assert_argument(argv, argc, i);
|
||||
o_root = argv[i + 1];
|
||||
i++;
|
||||
} else if (option[0] == '-' || !o_package.empty()) {
|
||||
throw runtime_error("invalid option " + option);
|
||||
} else {
|
||||
o_package = option;
|
||||
}
|
||||
}
|
||||
|
||||
if (o_package.empty())
|
||||
throw runtime_error("option missing");
|
||||
|
||||
//
|
||||
// Check UID
|
||||
//
|
||||
if (getuid())
|
||||
throw runtime_error("only root can remove packages");
|
||||
|
||||
//
|
||||
// Remove package
|
||||
//
|
||||
{
|
||||
db_lock lock(o_root, true);
|
||||
db_open(o_root);
|
||||
|
||||
if (!db_find_pkg(o_package))
|
||||
throw runtime_error("package " + o_package + " not installed");
|
||||
|
||||
db_rm_pkg(o_package);
|
||||
ldconfig();
|
||||
db_commit();
|
||||
}
|
||||
}
|
||||
|
||||
void pkgrm::print_help() const
|
||||
{
|
||||
cout << "usage: " << utilname << " [options] <package>" << endl
|
||||
<< "options:" << endl
|
||||
<< " -r, --root <path> specify alternative installation root" << endl
|
||||
<< " -v, --version print version and exit" << endl
|
||||
<< " -h, --help print help and exit" << endl;
|
||||
}
|
34
pkgrm.h
Normal file
34
pkgrm.h
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// pkgutils
|
||||
//
|
||||
// Copyright (c) 2000-2005 Per Liden
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
// USA.
|
||||
//
|
||||
|
||||
#ifndef PKGRM_H
|
||||
#define PKGRM_H
|
||||
|
||||
#include "pkgutil.h"
|
||||
|
||||
class pkgrm : public pkgutil {
|
||||
public:
|
||||
pkgrm() : pkgutil("pkgrm") {}
|
||||
virtual void run(int argc, char** argv);
|
||||
virtual void print_help() const;
|
||||
};
|
||||
|
||||
#endif /* PKGRM_H */
|
754
pkgutil.cc
Normal file
754
pkgutil.cc
Normal file
@ -0,0 +1,754 @@
|
||||
//
|
||||
// pkgutils
|
||||
//
|
||||
// Copyright (c) 2000-2005 Per Liden
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
// USA.
|
||||
//
|
||||
|
||||
#include "pkgutil.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <ext/stdio_filebuf.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <zlib.h>
|
||||
#include <libgen.h>
|
||||
#include <libtar.h>
|
||||
|
||||
using __gnu_cxx::stdio_filebuf;
|
||||
|
||||
static tartype_t gztype = {
|
||||
(openfunc_t)unistd_gzopen,
|
||||
(closefunc_t)gzclose,
|
||||
(readfunc_t)gzread,
|
||||
(writefunc_t)gzwrite
|
||||
};
|
||||
|
||||
pkgutil::pkgutil(const string& name)
|
||||
: utilname(name)
|
||||
{
|
||||
// Ignore signals
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGHUP, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
sigaction(SIGQUIT, &sa, 0);
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
}
|
||||
|
||||
void pkgutil::db_open(const string& path)
|
||||
{
|
||||
// Read database
|
||||
root = trim_filename(path + "/");
|
||||
const string filename = root + PKG_DB;
|
||||
|
||||
int fd = open(filename.c_str(), O_RDONLY);
|
||||
if (fd == -1)
|
||||
throw runtime_error_with_errno("could not open " + filename);
|
||||
|
||||
stdio_filebuf<char> filebuf(fd, ios::in, getpagesize());
|
||||
istream in(&filebuf);
|
||||
if (!in)
|
||||
throw runtime_error_with_errno("could not read " + filename);
|
||||
|
||||
while (!in.eof()) {
|
||||
// Read record
|
||||
string name;
|
||||
pkginfo_t info;
|
||||
getline(in, name);
|
||||
getline(in, info.version);
|
||||
for (;;) {
|
||||
string file;
|
||||
getline(in, file);
|
||||
|
||||
if (file.empty())
|
||||
break; // End of record
|
||||
|
||||
info.files.insert(info.files.end(), file);
|
||||
}
|
||||
if (!info.files.empty())
|
||||
packages[name] = info;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << packages.size() << " packages found in database" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void pkgutil::db_commit()
|
||||
{
|
||||
const string dbfilename = root + PKG_DB;
|
||||
const string dbfilename_new = dbfilename + ".incomplete_transaction";
|
||||
const string dbfilename_bak = dbfilename + ".backup";
|
||||
|
||||
// Remove failed transaction (if it exists)
|
||||
if (unlink(dbfilename_new.c_str()) == -1 && errno != ENOENT)
|
||||
throw runtime_error_with_errno("could not remove " + dbfilename_new);
|
||||
|
||||
// Write new database
|
||||
int fd_new = creat(dbfilename_new.c_str(), 0444);
|
||||
if (fd_new == -1)
|
||||
throw runtime_error_with_errno("could not create " + dbfilename_new);
|
||||
|
||||
stdio_filebuf<char> filebuf_new(fd_new, ios::out, getpagesize());
|
||||
ostream db_new(&filebuf_new);
|
||||
for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i) {
|
||||
if (!i->second.files.empty()) {
|
||||
db_new << i->first << "\n";
|
||||
db_new << i->second.version << "\n";
|
||||
copy(i->second.files.begin(), i->second.files.end(), ostream_iterator<string>(db_new, "\n"));
|
||||
db_new << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
db_new.flush();
|
||||
|
||||
// Make sure the new database was successfully written
|
||||
if (!db_new)
|
||||
throw runtime_error("could not write " + dbfilename_new);
|
||||
|
||||
// Synchronize file to disk
|
||||
if (fsync(fd_new) == -1)
|
||||
throw runtime_error_with_errno("could not synchronize " + dbfilename_new);
|
||||
|
||||
// Relink database backup
|
||||
if (unlink(dbfilename_bak.c_str()) == -1 && errno != ENOENT)
|
||||
throw runtime_error_with_errno("could not remove " + dbfilename_bak);
|
||||
if (link(dbfilename.c_str(), dbfilename_bak.c_str()) == -1)
|
||||
throw runtime_error_with_errno("could not create " + dbfilename_bak);
|
||||
|
||||
// Move new database into place
|
||||
if (rename(dbfilename_new.c_str(), dbfilename.c_str()) == -1)
|
||||
throw runtime_error_with_errno("could not rename " + dbfilename_new + " to " + dbfilename);
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << packages.size() << " packages written to database" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void pkgutil::db_add_pkg(const string& name, const pkginfo_t& info)
|
||||
{
|
||||
packages[name] = info;
|
||||
}
|
||||
|
||||
bool pkgutil::db_find_pkg(const string& name)
|
||||
{
|
||||
return (packages.find(name) != packages.end());
|
||||
}
|
||||
|
||||
void pkgutil::db_rm_pkg(const string& name)
|
||||
{
|
||||
set<string> files = packages[name].files;
|
||||
packages.erase(name);
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Removing package phase 1 (all files in package):" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
// Don't delete files that still have references
|
||||
for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i)
|
||||
for (set<string>::const_iterator j = i->second.files.begin(); j != i->second.files.end(); ++j)
|
||||
files.erase(*j);
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Removing package phase 2 (files that still have references excluded):" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
// Delete the files
|
||||
for (set<string>::const_reverse_iterator i = files.rbegin(); i != files.rend(); ++i) {
|
||||
const string filename = root + *i;
|
||||
if (file_exists(filename) && remove(filename.c_str()) == -1) {
|
||||
const char* msg = strerror(errno);
|
||||
cerr << utilname << ": could not remove " << filename << ": " << msg << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pkgutil::db_rm_pkg(const string& name, const set<string>& keep_list)
|
||||
{
|
||||
set<string> files = packages[name].files;
|
||||
packages.erase(name);
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Removing package phase 1 (all files in package):" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
// Don't delete files found in the keep list
|
||||
for (set<string>::const_iterator i = keep_list.begin(); i != keep_list.end(); ++i)
|
||||
files.erase(*i);
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Removing package phase 2 (files that is in the keep list excluded):" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
// Don't delete files that still have references
|
||||
for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i)
|
||||
for (set<string>::const_iterator j = i->second.files.begin(); j != i->second.files.end(); ++j)
|
||||
files.erase(*j);
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Removing package phase 3 (files that still have references excluded):" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
// Delete the files
|
||||
for (set<string>::const_reverse_iterator i = files.rbegin(); i != files.rend(); ++i) {
|
||||
const string filename = root + *i;
|
||||
if (file_exists(filename) && remove(filename.c_str()) == -1) {
|
||||
if (errno == ENOTEMPTY)
|
||||
continue;
|
||||
const char* msg = strerror(errno);
|
||||
cerr << utilname << ": could not remove " << filename << ": " << msg << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pkgutil::db_rm_files(set<string> files, const set<string>& keep_list)
|
||||
{
|
||||
// Remove all references
|
||||
for (packages_t::iterator i = packages.begin(); i != packages.end(); ++i)
|
||||
for (set<string>::const_iterator j = files.begin(); j != files.end(); ++j)
|
||||
i->second.files.erase(*j);
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Removing files:" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
// Don't delete files found in the keep list
|
||||
for (set<string>::const_iterator i = keep_list.begin(); i != keep_list.end(); ++i)
|
||||
files.erase(*i);
|
||||
|
||||
// Delete the files
|
||||
for (set<string>::const_reverse_iterator i = files.rbegin(); i != files.rend(); ++i) {
|
||||
const string filename = root + *i;
|
||||
if (file_exists(filename) && remove(filename.c_str()) == -1) {
|
||||
if (errno == ENOTEMPTY)
|
||||
continue;
|
||||
const char* msg = strerror(errno);
|
||||
cerr << utilname << ": could not remove " << filename << ": " << msg << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set<string> pkgutil::db_find_conflicts(const string& name, const pkginfo_t& info)
|
||||
{
|
||||
set<string> files;
|
||||
|
||||
// Find conflicting files in database
|
||||
for (packages_t::const_iterator i = packages.begin(); i != packages.end(); ++i) {
|
||||
if (i->first != name) {
|
||||
set_intersection(info.files.begin(), info.files.end(),
|
||||
i->second.files.begin(), i->second.files.end(),
|
||||
inserter(files, files.end()));
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Conflicts phase 1 (conflicts in database):" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
// Find conflicting files in filesystem
|
||||
for (set<string>::iterator i = info.files.begin(); i != info.files.end(); ++i) {
|
||||
const string filename = root + *i;
|
||||
if (file_exists(filename) && files.find(*i) == files.end())
|
||||
files.insert(files.end(), *i);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Conflicts phase 2 (conflicts in filesystem added):" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
// Exclude directories
|
||||
set<string> tmp = files;
|
||||
for (set<string>::const_iterator i = tmp.begin(); i != tmp.end(); ++i) {
|
||||
if ((*i)[i->length() - 1] == '/')
|
||||
files.erase(*i);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Conflicts phase 3 (directories excluded):" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
|
||||
// If this is an upgrade, remove files already owned by this package
|
||||
if (packages.find(name) != packages.end()) {
|
||||
for (set<string>::const_iterator i = packages[name].files.begin(); i != packages[name].files.end(); ++i)
|
||||
files.erase(*i);
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "Conflicts phase 4 (files already owned by this package excluded):" << endl;
|
||||
copy(files.begin(), files.end(), ostream_iterator<string>(cerr, "\n"));
|
||||
cerr << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
pair<string, pkgutil::pkginfo_t> pkgutil::pkg_open(const string& filename) const
|
||||
{
|
||||
pair<string, pkginfo_t> result;
|
||||
unsigned int i;
|
||||
TAR* t;
|
||||
|
||||
// Extract name and version from filename
|
||||
string basename(filename, filename.rfind('/') + 1);
|
||||
string name(basename, 0, basename.find(VERSION_DELIM));
|
||||
string version(basename, 0, basename.rfind(PKG_EXT));
|
||||
version.erase(0, version.find(VERSION_DELIM) == string::npos ? string::npos : version.find(VERSION_DELIM) + 1);
|
||||
|
||||
if (name.empty() || version.empty())
|
||||
throw runtime_error("could not determine name and/or version of " + basename + ": Invalid package name");
|
||||
|
||||
result.first = name;
|
||||
result.second.version = version;
|
||||
|
||||
if (tar_open(&t, const_cast<char*>(filename.c_str()), &gztype, O_RDONLY, 0, TAR_GNU) == -1)
|
||||
throw runtime_error_with_errno("could not open " + filename);
|
||||
|
||||
for (i = 0; !th_read(t); ++i) {
|
||||
result.second.files.insert(result.second.files.end(), th_get_pathname(t));
|
||||
if (TH_ISREG(t) && tar_skip_regfile(t))
|
||||
throw runtime_error_with_errno("could not read " + filename);
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
if (errno == 0)
|
||||
throw runtime_error("empty package");
|
||||
else
|
||||
throw runtime_error("could not read " + filename);
|
||||
}
|
||||
|
||||
tar_close(t);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void pkgutil::pkg_install(const string& filename, const set<string>& keep_list) const
|
||||
{
|
||||
TAR* t;
|
||||
unsigned int i;
|
||||
|
||||
if (tar_open(&t, const_cast<char*>(filename.c_str()), &gztype, O_RDONLY, 0, TAR_GNU) == -1)
|
||||
throw runtime_error_with_errno("could not open " + filename);
|
||||
|
||||
for (i = 0; !th_read(t); ++i) {
|
||||
string archive_filename = th_get_pathname(t);
|
||||
string reject_dir = trim_filename(root + string("/") + string(PKG_REJECTED));
|
||||
string original_filename = trim_filename(root + string("/") + archive_filename);
|
||||
string real_filename = original_filename;
|
||||
|
||||
// Check if file should be rejected
|
||||
if (file_exists(real_filename) && keep_list.find(archive_filename) != keep_list.end())
|
||||
real_filename = trim_filename(reject_dir + string("/") + archive_filename);
|
||||
|
||||
// Extract file
|
||||
if (tar_extract_file(t, const_cast<char*>(real_filename.c_str()))) {
|
||||
// If a file fails to install we just print an error message and
|
||||
// continue trying to install the rest of the package.
|
||||
const char* msg = strerror(errno);
|
||||
cerr << utilname << ": could not install " + archive_filename << ": " << msg << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check rejected file
|
||||
if (real_filename != original_filename) {
|
||||
bool remove_file = false;
|
||||
|
||||
// Directory
|
||||
if (TH_ISDIR(t))
|
||||
remove_file = permissions_equal(real_filename, original_filename);
|
||||
// Other files
|
||||
else
|
||||
remove_file = permissions_equal(real_filename, original_filename) &&
|
||||
(file_empty(real_filename) || file_equal(real_filename, original_filename));
|
||||
|
||||
// Remove rejected file or signal about its existence
|
||||
if (remove_file)
|
||||
file_remove(reject_dir, real_filename);
|
||||
else
|
||||
cout << utilname << ": rejecting " << archive_filename << ", keeping existing version" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
if (errno == 0)
|
||||
throw runtime_error("empty package");
|
||||
else
|
||||
throw runtime_error("could not read " + filename);
|
||||
}
|
||||
|
||||
tar_close(t);
|
||||
}
|
||||
|
||||
void pkgutil::ldconfig() const
|
||||
{
|
||||
// Only execute ldconfig if /etc/ld.so.conf exists
|
||||
if (file_exists(root + LDCONFIG_CONF)) {
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1)
|
||||
throw runtime_error_with_errno("fork() failed");
|
||||
|
||||
if (pid == 0) {
|
||||
execl(LDCONFIG, LDCONFIG, "-r", root.c_str(), 0);
|
||||
const char* msg = strerror(errno);
|
||||
cerr << utilname << ": could not execute " << LDCONFIG << ": " << msg << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
if (waitpid(pid, 0, 0) == -1)
|
||||
throw runtime_error_with_errno("waitpid() failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pkgutil::pkg_footprint(string& filename) const
|
||||
{
|
||||
unsigned int i;
|
||||
TAR* t;
|
||||
|
||||
if (tar_open(&t, const_cast<char*>(filename.c_str()), &gztype, O_RDONLY, 0, TAR_GNU) == -1)
|
||||
throw runtime_error_with_errno("could not open " + filename);
|
||||
|
||||
for (i = 0; !th_read(t); ++i) {
|
||||
// Access permissions
|
||||
if (TH_ISSYM(t)) {
|
||||
// Access permissions on symlinks differ among filesystems, e.g. XFS and ext2 have different.
|
||||
// To avoid getting different footprints we always use "lrwxrwxrwx".
|
||||
cout << "lrwxrwxrwx";
|
||||
} else {
|
||||
cout << mtos(th_get_mode(t));
|
||||
}
|
||||
|
||||
cout << '\t';
|
||||
|
||||
// User
|
||||
uid_t uid = th_get_uid(t);
|
||||
struct passwd* pw = getpwuid(uid);
|
||||
if (pw)
|
||||
cout << pw->pw_name;
|
||||
else
|
||||
cout << uid;
|
||||
|
||||
cout << '/';
|
||||
|
||||
// Group
|
||||
gid_t gid = th_get_gid(t);
|
||||
struct group* gr = getgrgid(gid);
|
||||
if (gr)
|
||||
cout << gr->gr_name;
|
||||
else
|
||||
cout << gid;
|
||||
|
||||
// Filename
|
||||
cout << '\t' << th_get_pathname(t);
|
||||
|
||||
// Special cases
|
||||
if (TH_ISSYM(t)) {
|
||||
// Symlink
|
||||
cout << " -> " << th_get_linkname(t);
|
||||
} else if (TH_ISCHR(t) || TH_ISBLK(t)) {
|
||||
// Device
|
||||
cout << " (" << th_get_devmajor(t) << ", " << th_get_devminor(t) << ")";
|
||||
} else if (TH_ISREG(t) && !th_get_size(t)) {
|
||||
// Empty regular file
|
||||
cout << " (EMPTY)";
|
||||
}
|
||||
|
||||
cout << '\n';
|
||||
|
||||
if (TH_ISREG(t) && tar_skip_regfile(t))
|
||||
throw runtime_error_with_errno("could not read " + filename);
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
if (errno == 0)
|
||||
throw runtime_error("empty package");
|
||||
else
|
||||
throw runtime_error("could not read " + filename);
|
||||
}
|
||||
|
||||
tar_close(t);
|
||||
}
|
||||
|
||||
void pkgutil::print_version() const
|
||||
{
|
||||
cout << utilname << " (pkgutils) " << VERSION << endl;
|
||||
}
|
||||
|
||||
db_lock::db_lock(const string& root, bool exclusive)
|
||||
: dir(0)
|
||||
{
|
||||
const string dirname = trim_filename(root + string("/") + PKG_DIR);
|
||||
|
||||
if (!(dir = opendir(dirname.c_str())))
|
||||
throw runtime_error_with_errno("could not read directory " + dirname);
|
||||
|
||||
if (flock(dirfd(dir), (exclusive ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
throw runtime_error("package database is currently locked by another process");
|
||||
else
|
||||
throw runtime_error_with_errno("could not lock directory " + dirname);
|
||||
}
|
||||
}
|
||||
|
||||
db_lock::~db_lock()
|
||||
{
|
||||
if (dir) {
|
||||
flock(dirfd(dir), LOCK_UN);
|
||||
closedir(dir);
|
||||
}
|
||||
}
|
||||
|
||||
void assert_argument(char** argv, int argc, int index)
|
||||
{
|
||||
if (argc - 1 < index + 1)
|
||||
throw runtime_error("option " + string(argv[index]) + " requires an argument");
|
||||
}
|
||||
|
||||
string itos(unsigned int value)
|
||||
{
|
||||
static char buf[20];
|
||||
sprintf(buf, "%u", value);
|
||||
return buf;
|
||||
}
|
||||
|
||||
string mtos(mode_t mode)
|
||||
{
|
||||
string s;
|
||||
|
||||
// File type
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFREG: s += '-'; break; // Regular
|
||||
case S_IFDIR: s += 'd'; break; // Directory
|
||||
case S_IFLNK: s += 'l'; break; // Symbolic link
|
||||
case S_IFCHR: s += 'c'; break; // Character special
|
||||
case S_IFBLK: s += 'b'; break; // Block special
|
||||
case S_IFSOCK: s += 's'; break; // Socket
|
||||
case S_IFIFO: s += 'p'; break; // Fifo
|
||||
default: s += '?'; break; // Unknown
|
||||
}
|
||||
|
||||
// User permissions
|
||||
s += (mode & S_IRUSR) ? 'r' : '-';
|
||||
s += (mode & S_IWUSR) ? 'w' : '-';
|
||||
switch (mode & (S_IXUSR | S_ISUID)) {
|
||||
case S_IXUSR: s += 'x'; break;
|
||||
case S_ISUID: s += 'S'; break;
|
||||
case S_IXUSR | S_ISUID: s += 's'; break;
|
||||
default: s += '-'; break;
|
||||
}
|
||||
|
||||
// Group permissions
|
||||
s += (mode & S_IRGRP) ? 'r' : '-';
|
||||
s += (mode & S_IWGRP) ? 'w' : '-';
|
||||
switch (mode & (S_IXGRP | S_ISGID)) {
|
||||
case S_IXGRP: s += 'x'; break;
|
||||
case S_ISGID: s += 'S'; break;
|
||||
case S_IXGRP | S_ISGID: s += 's'; break;
|
||||
default: s += '-'; break;
|
||||
}
|
||||
|
||||
// Other permissions
|
||||
s += (mode & S_IROTH) ? 'r' : '-';
|
||||
s += (mode & S_IWOTH) ? 'w' : '-';
|
||||
switch (mode & (S_IXOTH | S_ISVTX)) {
|
||||
case S_IXOTH: s += 'x'; break;
|
||||
case S_ISVTX: s += 'T'; break;
|
||||
case S_IXOTH | S_ISVTX: s += 't'; break;
|
||||
default: s += '-'; break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int unistd_gzopen(char* pathname, int flags, mode_t mode)
|
||||
{
|
||||
char* gz_mode;
|
||||
|
||||
switch (flags & O_ACCMODE) {
|
||||
case O_WRONLY:
|
||||
gz_mode = "w";
|
||||
break;
|
||||
|
||||
case O_RDONLY:
|
||||
gz_mode = "r";
|
||||
break;
|
||||
|
||||
case O_RDWR:
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd;
|
||||
gzFile gz_file;
|
||||
|
||||
if ((fd = open(pathname, flags, mode)) == -1)
|
||||
return -1;
|
||||
|
||||
if ((flags & O_CREAT) && fchmod(fd, mode))
|
||||
return -1;
|
||||
|
||||
if (!(gz_file = gzdopen(fd, gz_mode))) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)gz_file;
|
||||
}
|
||||
|
||||
string trim_filename(const string& filename)
|
||||
{
|
||||
string search("//");
|
||||
string result = filename;
|
||||
|
||||
for (string::size_type pos = result.find(search); pos != string::npos; pos = result.find(search))
|
||||
result.replace(pos, search.size(), "/");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool file_exists(const string& filename)
|
||||
{
|
||||
struct stat buf;
|
||||
return !lstat(filename.c_str(), &buf);
|
||||
}
|
||||
|
||||
bool file_empty(const string& filename)
|
||||
{
|
||||
struct stat buf;
|
||||
|
||||
if (lstat(filename.c_str(), &buf) == -1)
|
||||
return false;
|
||||
|
||||
return (S_ISREG(buf.st_mode) && buf.st_size == 0);
|
||||
}
|
||||
|
||||
bool file_equal(const string& file1, const string& file2)
|
||||
{
|
||||
struct stat buf1, buf2;
|
||||
|
||||
if (lstat(file1.c_str(), &buf1) == -1)
|
||||
return false;
|
||||
|
||||
if (lstat(file2.c_str(), &buf2) == -1)
|
||||
return false;
|
||||
|
||||
// Regular files
|
||||
if (S_ISREG(buf1.st_mode) && S_ISREG(buf2.st_mode)) {
|
||||
ifstream f1(file1.c_str());
|
||||
ifstream f2(file2.c_str());
|
||||
|
||||
if (!f1 || !f2)
|
||||
return false;
|
||||
|
||||
while (!f1.eof()) {
|
||||
char buffer1[4096];
|
||||
char buffer2[4096];
|
||||
f1.read(buffer1, 4096);
|
||||
f2.read(buffer2, 4096);
|
||||
if (f1.gcount() != f2.gcount() ||
|
||||
memcmp(buffer1, buffer2, f1.gcount()) ||
|
||||
f1.eof() != f2.eof())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// Symlinks
|
||||
else if (S_ISLNK(buf1.st_mode) && S_ISLNK(buf2.st_mode)) {
|
||||
char symlink1[MAXPATHLEN];
|
||||
char symlink2[MAXPATHLEN];
|
||||
|
||||
memset(symlink1, 0, MAXPATHLEN);
|
||||
memset(symlink2, 0, MAXPATHLEN);
|
||||
|
||||
if (readlink(file1.c_str(), symlink1, MAXPATHLEN - 1) == -1)
|
||||
return false;
|
||||
|
||||
if (readlink(file2.c_str(), symlink2, MAXPATHLEN - 1) == -1)
|
||||
return false;
|
||||
|
||||
return !strncmp(symlink1, symlink2, MAXPATHLEN);
|
||||
}
|
||||
// Character devices
|
||||
else if (S_ISCHR(buf1.st_mode) && S_ISCHR(buf2.st_mode)) {
|
||||
return buf1.st_dev == buf2.st_dev;
|
||||
}
|
||||
// Block devices
|
||||
else if (S_ISBLK(buf1.st_mode) && S_ISBLK(buf2.st_mode)) {
|
||||
return buf1.st_dev == buf2.st_dev;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool permissions_equal(const string& file1, const string& file2)
|
||||
{
|
||||
struct stat buf1;
|
||||
struct stat buf2;
|
||||
|
||||
if (lstat(file1.c_str(), &buf1) == -1)
|
||||
return false;
|
||||
|
||||
if (lstat(file2.c_str(), &buf2) == -1)
|
||||
return false;
|
||||
|
||||
return(buf1.st_mode == buf2.st_mode) &&
|
||||
(buf1.st_uid == buf2.st_uid) &&
|
||||
(buf1.st_gid == buf2.st_gid);
|
||||
}
|
||||
|
||||
void file_remove(const string& basedir, const string& filename)
|
||||
{
|
||||
if (filename != basedir && !remove(filename.c_str())) {
|
||||
char* path = strdup(filename.c_str());
|
||||
file_remove(basedir, dirname(path));
|
||||
free(path);
|
||||
}
|
||||
}
|
108
pkgutil.h
Normal file
108
pkgutil.h
Normal file
@ -0,0 +1,108 @@
|
||||
//
|
||||
// pkgutils
|
||||
//
|
||||
// Copyright (c) 2000-2005 Per Liden
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
// USA.
|
||||
//
|
||||
|
||||
#ifndef PKGUTIL_H
|
||||
#define PKGUTIL_H
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#define PKG_EXT ".pkg.tar.gz"
|
||||
#define PKG_DIR "var/lib/pkg"
|
||||
#define PKG_DB "var/lib/pkg/db"
|
||||
#define PKG_REJECTED "var/lib/pkg/rejected"
|
||||
#define VERSION_DELIM '#'
|
||||
#define LDCONFIG "/sbin/ldconfig"
|
||||
#define LDCONFIG_CONF "/etc/ld.so.conf"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class pkgutil {
|
||||
public:
|
||||
struct pkginfo_t {
|
||||
string version;
|
||||
set<string> files;
|
||||
};
|
||||
|
||||
typedef map<string, pkginfo_t> packages_t;
|
||||
|
||||
explicit pkgutil(const string& name);
|
||||
virtual ~pkgutil() {}
|
||||
virtual void run(int argc, char** argv) = 0;
|
||||
virtual void print_help() const = 0;
|
||||
void print_version() const;
|
||||
|
||||
protected:
|
||||
// Database
|
||||
void db_open(const string& path);
|
||||
void db_commit();
|
||||
void db_add_pkg(const string& name, const pkginfo_t& info);
|
||||
bool db_find_pkg(const string& name);
|
||||
void db_rm_pkg(const string& name);
|
||||
void db_rm_pkg(const string& name, const set<string>& keep_list);
|
||||
void db_rm_files(set<string> files, const set<string>& keep_list);
|
||||
set<string> db_find_conflicts(const string& name, const pkginfo_t& info);
|
||||
|
||||
// Tar.gz
|
||||
pair<string, pkginfo_t> pkg_open(const string& filename) const;
|
||||
void pkg_install(const string& filename, const set<string>& keep_list) const;
|
||||
void pkg_footprint(string& filename) const;
|
||||
void ldconfig() const;
|
||||
|
||||
string utilname;
|
||||
packages_t packages;
|
||||
string root;
|
||||
};
|
||||
|
||||
class db_lock {
|
||||
public:
|
||||
db_lock(const string& root, bool exclusive);
|
||||
~db_lock();
|
||||
private:
|
||||
DIR* dir;
|
||||
};
|
||||
|
||||
class runtime_error_with_errno : public runtime_error {
|
||||
public:
|
||||
explicit runtime_error_with_errno(const string& msg) throw()
|
||||
: runtime_error(msg + string(": ") + strerror(errno)) {}
|
||||
};
|
||||
|
||||
// Utility functions
|
||||
void assert_argument(char** argv, int argc, int index);
|
||||
string itos(unsigned int value);
|
||||
string mtos(mode_t mode);
|
||||
int unistd_gzopen(char* pathname, int flags, mode_t mode);
|
||||
string trim_filename(const string& filename);
|
||||
bool file_exists(const string& filename);
|
||||
bool file_empty(const string& filename);
|
||||
bool file_equal(const string& file1, const string& file2);
|
||||
bool permissions_equal(const string& file1, const string& file2);
|
||||
void file_remove(const string& basedir, const string& filename);
|
||||
|
||||
#endif /* PKGUTIL_H */
|
77
rejmerge.8.in
Normal file
77
rejmerge.8.in
Normal file
@ -0,0 +1,77 @@
|
||||
.TH rejmerge 8 "" "pkgutils #VERSION#" ""
|
||||
.SH NAME
|
||||
rejmerge \- merge files that were rejected during package upgrades
|
||||
.SH SYNOPSIS
|
||||
\fBrejmerge [options]\fP
|
||||
.SH DESCRIPTION
|
||||
\fBrejmerge\fP is a \fIpackage management\fP utility that helps you merge files that were rejected
|
||||
during package upgrades. For each rejected file found in \fI/var/lib/pkg/rejected/\fP, \fBrejmerge\fP
|
||||
will display the difference between the installed version and the rejected version. The user can then
|
||||
choose to keep the installed version, upgrade to the rejected version or perform a merge of the two.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B "\-r, \-\-root <path>"
|
||||
Specify alternative root (default is "/"). This should be used
|
||||
if you want to merge rejected files on a temporary mounted partition,
|
||||
which is "owned" by another system.
|
||||
.TP
|
||||
.B "\-v, \-\-version"
|
||||
Print version and exit.
|
||||
.TP
|
||||
.B "\-h, \-\-help"
|
||||
Print help and exit.
|
||||
.SH CONFIGURATION
|
||||
When \fBrejmerge\fP is started it will source \fI/etc/rejmerge.conf\fP.
|
||||
This file can be used to alter the way \fBrejmerge\fP displays file differences and performs file
|
||||
merges. Changing the default behaviour is done by re-defining the shell functions \fBrejmerge_diff()\fP
|
||||
and/or \fBrejmerge_merge()\fP.
|
||||
.TP
|
||||
.B rejmerge_diff()
|
||||
This function is executed once for each rejected file. Arguments \fB$1\fP and \fB$2\fP contain the paths
|
||||
to the installed and rejected files. Argument \fB$3\fP contains the path to a temporary file where this
|
||||
function should write its result. The contents of the temporary file will later be presented to the user
|
||||
as the difference between the two files.
|
||||
.TP
|
||||
.B rejmerge_merge()
|
||||
This function is executed when the user chooses to merge two files. Arguments \fB$1\fP and \fB$2\fP
|
||||
contain the paths to the installed and rejected files. Argument \fB$3\fP contains the path to a temporary
|
||||
file where this function should write its result. The contents of the temporary file will later be
|
||||
presented to the user as the merge result.
|
||||
This function also has the option to set the variable \fB$REJMERGE_MERGE_INFO\fP. The contents of this
|
||||
variable will be displayed as informational text after a merge has been performed. Its purpose is to provide
|
||||
information about the merge, e.g. "5 merge conflicts found".
|
||||
|
||||
.PP
|
||||
Example:
|
||||
|
||||
.nf
|
||||
#
|
||||
# /etc/rejmerge.conf: rejmerge(8) configuration
|
||||
#
|
||||
|
||||
rejmerge_diff() {
|
||||
# Use diff(1) to produce side-by-side output
|
||||
diff -y $1 $2 > $3
|
||||
}
|
||||
|
||||
rejmerge_merge() {
|
||||
# Use sdiff(1) to merge
|
||||
sdiff -o $3 $1 $2
|
||||
}
|
||||
|
||||
# End of file
|
||||
.fi
|
||||
|
||||
.SH FILES
|
||||
.TP
|
||||
.B "/etc/rejmerge.conf"
|
||||
Configuration file.
|
||||
.TP
|
||||
.B "/var/lib/pkg/rejected/"
|
||||
Directory where rejected files are stored.
|
||||
.SH SEE ALSO
|
||||
pkgadd(8), pkgrm(8), pkginfo(8), pkgmk(8)
|
||||
.SH COPYRIGHT
|
||||
rejmerge (pkgutils) is Copyright (c) 2000-2005 Per Liden and is licensed through
|
||||
the GNU General Public License. Read the COPYING file for the complete license.
|
5
rejmerge.conf
Normal file
5
rejmerge.conf
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# /etc/rejmerge.conf: rejmerge(8) configuration
|
||||
#
|
||||
|
||||
# End of file
|
315
rejmerge.in
Executable file
315
rejmerge.in
Executable file
@ -0,0 +1,315 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# rejmerge (pkgutils)
|
||||
#
|
||||
# Copyright (c) 2000-2005 Per Liden
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
# USA.
|
||||
#
|
||||
|
||||
info_n() {
|
||||
echo -n "=======> $1"
|
||||
}
|
||||
|
||||
info() {
|
||||
info_n "$1"
|
||||
echo
|
||||
}
|
||||
|
||||
interrupted() {
|
||||
echo ""
|
||||
info "Aborted."
|
||||
exit 1
|
||||
}
|
||||
|
||||
atexit() {
|
||||
if [ -e "$TMPFILE" ]; then
|
||||
rm -f "$TMPFILE"
|
||||
fi
|
||||
}
|
||||
|
||||
rejmerge_diff() {
|
||||
diff -u "$1" "$2" > "$3"
|
||||
}
|
||||
|
||||
rejmerge_merge() {
|
||||
diff --old-group-format="%<" \
|
||||
--new-group-format="%>" \
|
||||
--changed-group-format="<<<<< MERGE CONFLICT $1 >>>>>
|
||||
%<<<<<< MERGE CONFLICT $2 >>>>>
|
||||
%><<<<< END MERGE CONFLICT >>>>>
|
||||
" \
|
||||
"$1" "$2" > "$3"
|
||||
|
||||
REJMERGE_MERGE_INFO="$(grep -c '^<<<<< END MERGE CONFLICT >>>>>$' "$3") merge conflict(s)."
|
||||
}
|
||||
|
||||
permissions_menu() {
|
||||
while true; do
|
||||
info "Access permissions $1"
|
||||
stat -c '%A %U %G %n' "$1"
|
||||
stat -c '%A %U %G %n' "$2"
|
||||
while true; do
|
||||
info_n "[K]eep [U]pgrade [D]iff [S]kip? "
|
||||
read -n1 CMD
|
||||
echo
|
||||
|
||||
case "$CMD" in
|
||||
k|K) chown --reference="$1" "$2"
|
||||
chmod --reference="$1" "$2"
|
||||
break 2
|
||||
;;
|
||||
u|U) chown --reference="$2" "$1"
|
||||
chmod --reference="$2" "$1"
|
||||
break 2
|
||||
;;
|
||||
d|D) break 1
|
||||
;;
|
||||
s|S) break 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
merge_menu() {
|
||||
rejmerge_merge "$1" "$2" "$TMPFILE"
|
||||
|
||||
while true; do
|
||||
info "Merged $1"
|
||||
cat "$TMPFILE" | more
|
||||
|
||||
if [ "$REJMERGE_MERGE_INFO" ]; then
|
||||
info "$REJMERGE_MERGE_INFO"
|
||||
unset REJMERGE_MERGE_INFO
|
||||
fi
|
||||
|
||||
while true; do
|
||||
info_n "[I]nstall [E]dit [V]iew [S]kip? "
|
||||
read -n1 CMD
|
||||
echo
|
||||
|
||||
case "$CMD" in
|
||||
i|I) chmod --reference="$1" "$TMPFILE"
|
||||
mv -f "$TMPFILE" "$1"
|
||||
rm -f "$2"
|
||||
break 2
|
||||
;;
|
||||
e|E) $EDITOR "$TMPFILE"
|
||||
break 1
|
||||
;;
|
||||
v|V) break 1
|
||||
;;
|
||||
s|S) break 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
: > "$TMPFILE"
|
||||
}
|
||||
|
||||
diff_menu() {
|
||||
rejmerge_diff "$1" "$2" "$TMPFILE"
|
||||
|
||||
while true; do
|
||||
info "$1"
|
||||
cat "$TMPFILE" | more
|
||||
while true; do
|
||||
info_n "[K]eep [U]pgrade [M]erge [D]iff [S]kip? "
|
||||
read -n1 CMD
|
||||
echo
|
||||
|
||||
case "$CMD" in
|
||||
k|K) rm -f "$2"
|
||||
break 2
|
||||
;;
|
||||
u|U) mv -f "$2" "$1"
|
||||
break 2
|
||||
;;
|
||||
m|M) merge_menu "$1" "$2"
|
||||
break 2
|
||||
;;
|
||||
d|D) break 1
|
||||
;;
|
||||
s|S) break 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
: > "$TMPFILE"
|
||||
}
|
||||
|
||||
file_menu() {
|
||||
while true; do
|
||||
info "$1"
|
||||
file "$1" "$2"
|
||||
while true; do
|
||||
info_n "[K]eep [U]pgrade [D]iff [S]kip? "
|
||||
read -n1 CMD
|
||||
echo
|
||||
|
||||
case "$CMD" in
|
||||
k|K) rm -f "$2"
|
||||
break 2
|
||||
;;
|
||||
u|U) mv -f "$2" "$1"
|
||||
break 2
|
||||
;;
|
||||
d|D) break 1
|
||||
;;
|
||||
s|S) break 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
print_help() {
|
||||
echo "usage: $REJMERGE_COMMAND [options]"
|
||||
echo "options:"
|
||||
echo " -r, --root <path> specify alternative root"
|
||||
echo " -v, --version print version and exit "
|
||||
echo " -h, --help print help and exit"
|
||||
}
|
||||
|
||||
parse_options() {
|
||||
while [ "$1" ]; do
|
||||
case $1 in
|
||||
-r|--root)
|
||||
if [ ! "$2" ]; then
|
||||
echo "$REJMERGE_COMMAND: option $1 requires an argument"
|
||||
exit 1
|
||||
fi
|
||||
REJMERGE_ROOT="$2"
|
||||
REJMERGE_CONF="$2$REJMERGE_CONF"
|
||||
REJECTED_DIR="$2$REJECTED_DIR"
|
||||
shift ;;
|
||||
-v|--version)
|
||||
echo "$REJMERGE_COMMAND (pkgutils) $REJMERGE_VERSION"
|
||||
exit 0 ;;
|
||||
-h|--help)
|
||||
print_help
|
||||
exit 0 ;;
|
||||
*)
|
||||
echo "$REJMERGE_COMMAND: invalid option $1"
|
||||
exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ ! -d "$REJECTED_DIR" ]; then
|
||||
echo "$REJMERGE_COMMAND: $REJECTED_DIR not found"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
files_regular() {
|
||||
local STAT_FILE1=$(stat -c '%F' "$1")
|
||||
local STAT_FILE2=$(stat -c '%F' "$2")
|
||||
|
||||
if [ "$STAT_FILE1" != "regular file" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$STAT_FILE2" != "regular file" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_options "$@"
|
||||
|
||||
if [ "$UID" != "0" ]; then
|
||||
echo "$REJMERGE_COMMAND: only root can merge rejected files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Read configuration
|
||||
if [ -f "$REJMERGE_CONF" ]; then
|
||||
. "$REJMERGE_CONF"
|
||||
fi
|
||||
|
||||
REJECTED_FILES_FOUND="no"
|
||||
|
||||
# Check files
|
||||
for REJECTED_FILE in $(find $REJECTED_DIR ! -type d); do
|
||||
INSTALLED_FILE="$REJMERGE_ROOT${REJECTED_FILE##$REJECTED_DIR}"
|
||||
|
||||
# Remove rejected file if there is no installed version
|
||||
if [ ! -e "$INSTALLED_FILE" ]; then
|
||||
rm -f "$REJECTED_FILE"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check permissions
|
||||
local STAT_FILE1=$(stat -c '%A %U %G' "$INSTALLED_FILE")
|
||||
local STAT_FILE2=$(stat -c '%A %U %G' "$REJECTED_FILE")
|
||||
|
||||
if [ "$STAT_FILE1" != "$STAT_FILE2" ]; then
|
||||
REJECTED_FILES_FOUND="yes"
|
||||
permissions_menu "$INSTALLED_FILE" "$REJECTED_FILE"
|
||||
fi
|
||||
|
||||
# Check file types
|
||||
if files_regular "$INSTALLED_FILE" "$REJECTED_FILE"; then
|
||||
# Both files are regular
|
||||
if cmp -s "$INSTALLED_FILE" "$REJECTED_FILE"; then
|
||||
rm -f "$REJECTED_FILE"
|
||||
else
|
||||
REJECTED_FILES_FOUND="yes"
|
||||
diff_menu "$INSTALLED_FILE" "$REJECTED_FILE"
|
||||
fi
|
||||
else
|
||||
# At least one file is non-regular
|
||||
REJECTED_FILES_FOUND="yes"
|
||||
file_menu "$INSTALLED_FILE" "$REJECTED_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove empty directories
|
||||
for DIR in $(find $REJECTED_DIR -depth -type d); do
|
||||
if [ "$DIR" != "$REJECTED_DIR" ]; then
|
||||
rmdir "$DIR" &> /dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$REJECTED_FILES_FOUND" = "no" ]; then
|
||||
echo "Nothing to merge"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM
|
||||
trap "atexit" EXIT
|
||||
|
||||
export LC_ALL=POSIX
|
||||
|
||||
readonly REJMERGE_VERSION="#VERSION#"
|
||||
readonly REJMERGE_COMMAND="${0##*/}"
|
||||
REJMERGE_ROOT=""
|
||||
REJMERGE_CONF="/etc/rejmerge.conf"
|
||||
REJECTED_DIR="/var/lib/pkg/rejected"
|
||||
EDITOR=${EDITOR:-vi}
|
||||
TMPFILE=$(mktemp) || exit 1
|
||||
|
||||
main "$@"
|
||||
|
||||
# End of file
|
Loading…
x
Reference in New Issue
Block a user