Remove CTM from 13-CURRENT after the release of FreeBSD-12.0.
The removal (and creation of a port) has been pre-announced in UPDATING 1 month ago. Packages are available for all supported FreeBSD vesions. I did not think that another entry in UPDATING is required to note the actual removal. No MFC is planned - CTM shall be kept in base for all releases up to 12.x. Reviewed by: rgrimes Approved by: imp, bcr (manpages) Relnotes: yes Differential Revision: https://reviews.freebsd.org/D17935
This commit is contained in:
parent
855acb84ca
commit
385e98080c
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=342126
@ -479,10 +479,6 @@ is set explicitly)
|
||||
Set to compile with CTF (Compact C Type Format) data.
|
||||
CTF data encapsulates a reduced form of debugging information
|
||||
similar to DWARF and the venerable stabs and is required for DTrace.
|
||||
.It Va WITHOUT_CTM
|
||||
Set to not build
|
||||
.Xr ctm 1
|
||||
and related utilities.
|
||||
.It Va WITHOUT_CUSE
|
||||
Set to not build CUSE-related programs and libraries.
|
||||
.It Va WITHOUT_CXGBETOOL
|
||||
|
@ -1551,18 +1551,6 @@ OLD_FILES+=usr/bin/cpp
|
||||
OLD_FILES+=usr/share/man/man1/cpp.1.gz
|
||||
.endif
|
||||
|
||||
.if ${MK_CTM} == no
|
||||
OLD_FILES+=usr/sbin/ctm
|
||||
OLD_FILES+=usr/sbin/ctm_dequeue
|
||||
OLD_FILES+=usr/sbin/ctm_rmail
|
||||
OLD_FILES+=usr/sbin/ctm_smail
|
||||
OLD_FILES+=usr/share/man/man1/ctm.1.gz
|
||||
OLD_FILES+=usr/share/man/man1/ctm_dequeue.1.gz
|
||||
OLD_FILES+=usr/share/man/man1/ctm_rmail.1.gz
|
||||
OLD_FILES+=usr/share/man/man1/ctm_smail.1.gz
|
||||
OLD_FILES+=usr/share/man/man5/ctm.5.gz
|
||||
.endif
|
||||
|
||||
.if ${MK_CUSE} == no
|
||||
OLD_FILES+=usr/include/fs/cuse/cuse_defs.h
|
||||
OLD_FILES+=usr/include/fs/cuse/cuse_ioctl.h
|
||||
|
@ -1,4 +0,0 @@
|
||||
.\" $FreeBSD$
|
||||
Set to not build
|
||||
.Xr ctm 1
|
||||
and related utilities.
|
@ -123,7 +123,6 @@ SUBDIR.${MK_BLUETOOTH}+= bluetooth
|
||||
SUBDIR.${MK_BOOTPARAMD}+= bootparamd
|
||||
SUBDIR.${MK_BSDINSTALL}+= bsdinstall
|
||||
SUBDIR.${MK_BSNMP}+= bsnmpd
|
||||
SUBDIR.${MK_CTM}+= ctm
|
||||
SUBDIR.${MK_CXGBETOOL}+= cxgbetool
|
||||
SUBDIR.${MK_DIALOG}+= bsdconfig
|
||||
SUBDIR.${MK_EFI}+= efivar efidp efibootmgr
|
||||
|
@ -1,5 +0,0 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SUBDIR= ctm ctm_rmail ctm_smail ctm_dequeue
|
||||
|
||||
.include <bsd.subdir.mk>
|
@ -1,5 +0,0 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.if exists(${.CURDIR:H:H}/Makefile.inc)
|
||||
.include "${.CURDIR:H:H}/Makefile.inc"
|
||||
.endif
|
@ -1,89 +0,0 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
# "THE BEER-WARE LICENSE" (Revision 42):
|
||||
# <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
# can do whatever you want with this stuff. If we meet some day, and you think
|
||||
# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
# ----------------------------------------------------------------------------
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
What will I not find in this file ?
|
||||
-----------------------------------
|
||||
Instructions on how to obtain FreeBSD via CTM.
|
||||
Contact <CTM@FreeBSD.org> for that.
|
||||
|
||||
What is CTM ?
|
||||
-------------
|
||||
CTM was originally "CVS Through eMail", but has since changed scope to be
|
||||
much more general.
|
||||
CTM is now meant to be the definitive way to make and apply a delta between
|
||||
two versions of a directory tree.
|
||||
There are two parts to this, making the delta and applying it. These are two
|
||||
entirely different things. CTM concentrates the computation-burden on the
|
||||
generation of the deltas, as a delta very often is applied more times than
|
||||
it is made. Second CTM tries to make the minimal size delta.
|
||||
|
||||
Why not use diff/patch ?
|
||||
------------------------
|
||||
Good question. Primarily because diff and patch doesn't do their job very
|
||||
well. They don't deal with binary files (in this case files with '\0' or
|
||||
'\0377' characters in them or files that doesn't end in '\n') which isn't
|
||||
a big surprise: they were made to deal with text-files only. As a second
|
||||
gripe, with patch you send the entire file to delete it. Not particular
|
||||
efficient.
|
||||
|
||||
So what does CTM do exactly ?
|
||||
-----------------------------
|
||||
CTM will produce a file, (a delta) containing the instructions and data needed
|
||||
to take another copy of the tree from the old to the new status. CTM means to
|
||||
do this in the exact sense, and therefore the delta contains MD5 checksums to
|
||||
verify that the tree it is applied to is indeed in the state CTM expects.
|
||||
|
||||
This means that if you have modified the tree locally, CTM might not be able
|
||||
to upgrade your copy.
|
||||
|
||||
How do I make a CTM-delta ?
|
||||
---------------------------
|
||||
|
||||
Read the source, and be prepared to have 2 copies of the tree; One is
|
||||
the reference ("From") tree, and the other is the delta ("To") tree.
|
||||
The mkCTM script will create the CTM diff of the differences between
|
||||
the reference tree and the delta tree. A lot of scratch space is
|
||||
required, and your machine will work hard.
|
||||
|
||||
How do I apply a CTM-delta ?
|
||||
----------------------------
|
||||
You pass it to the 'ctm' command. You can pass a CTM-delta on stdin, or
|
||||
you can give the filename as an argument. If you do the latter, you make
|
||||
life a lot easier for your self, since the program can accept gzip'ed files
|
||||
and since it will not have to make a temporary copy of your file. You can
|
||||
specify multiple deltas at one time, they will be processed one at a time.
|
||||
|
||||
The ctm command runs in a number of passes. It will process the entire
|
||||
input file in each pass, before commencing with the next pass.
|
||||
|
||||
Pass 1 will validate that the input file is OK. The syntax, the data and
|
||||
the global MD5 checksum will be checked. If any of these fail, ctm will
|
||||
never be able to do anything with the file, so it will simply reject it.
|
||||
|
||||
Pass 2 will validate that the directory tree is in the state expected by
|
||||
the CTM-delta. This is done by looking for files and directories which
|
||||
should/should not exists and by checking the MD5 checksums of files.
|
||||
|
||||
Pass 3 will actually apply the delta.
|
||||
|
||||
Should I delete the delta when I have applied it ?
|
||||
--------------------------------------------------
|
||||
No. You might want to selectively reconstruct a file latter on.
|
||||
|
||||
Why is CTM not being maintained?
|
||||
--------------------------------
|
||||
Because CVSUP has improved on the concept quite a bit, and is now
|
||||
the method of choice.
|
||||
|
||||
Is there a pointer to this whole glorious saga?
|
||||
-----------------------------------------------
|
||||
You bet! http://phk.freebsd.dk/sagas/ctm.html
|
||||
|
||||
Poul-Henning
|
@ -1,24 +0,0 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
# "THE BEER-WARE LICENSE" (Revision 42):
|
||||
# <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
# can do whatever you want with this stuff. If we meet some day, and you think
|
||||
# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
# ----------------------------------------------------------------------------
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= ctm
|
||||
MAN= ctm.1 ctm.5
|
||||
SRCS= ctm.c ctm_input.c ctm_pass1.c ctm_pass2.c ctm_pass3.c \
|
||||
ctm_passb.c ctm_syntax.c ctm_ed.c
|
||||
|
||||
NOTYET= ctm_ed.c
|
||||
|
||||
LIBADD= md
|
||||
|
||||
WARNS?= 2
|
||||
|
||||
.if exists(${.CURDIR:H:H}/Makefile.inc)
|
||||
.include "${.CURDIR:H:H}/Makefile.inc"
|
||||
.endif
|
||||
.include <bsd.prog.mk>
|
@ -1,18 +0,0 @@
|
||||
# $FreeBSD$
|
||||
# Autogenerated - do NOT edit!
|
||||
|
||||
DIRDEPS = \
|
||||
gnu/lib/csu \
|
||||
include \
|
||||
include/xlocale \
|
||||
lib/${CSU_DIR} \
|
||||
lib/libc \
|
||||
lib/libcompiler_rt \
|
||||
lib/libmd \
|
||||
|
||||
|
||||
.include <dirdeps.mk>
|
||||
|
||||
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||
# local dependencies - needed for -jN in clean tree
|
||||
.endif
|
@ -1,336 +0,0 @@
|
||||
.\" ----------------------------------------------------------------------------
|
||||
.\" "THE BEER-WARE LICENSE" (Revision 42):
|
||||
.\" <joerg@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
.\" can do whatever you want with this stuff. If we meet some day, and you think
|
||||
.\" this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
.\" ----------------------------------------------------------------------------
|
||||
.\"
|
||||
.\" This manual page is partially obtained from Poul-Hennings CTM README
|
||||
.\" file.
|
||||
.\"
|
||||
.\" CTM and ctm(1) by <phk@FreeBSD.org>
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd November 15, 2018
|
||||
.Dt CTM 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm ctm
|
||||
.Nd source code mirror program
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl cFklquv
|
||||
.Op Fl b Ar basedir
|
||||
.Op Fl B Ar backup-file
|
||||
.Op Fl e Ar include-regex
|
||||
.Op Fl t Ar tar-command
|
||||
.Op Fl T Ar tmpdir
|
||||
.Op Fl V Ar level
|
||||
.Op Fl x Ar exclude-regex
|
||||
.Ar
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility was originally
|
||||
.Dq Cvs Through eMail ,
|
||||
but now instead it seems more fitting to call it
|
||||
.Dq Current Through eMail .
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
utility is now meant to be the definitive way to make and apply a delta between
|
||||
two versions of a directory tree.
|
||||
.Pp
|
||||
There are two parts to this, making the delta and applying it.
|
||||
These are two
|
||||
entirely different things.
|
||||
.Ss Usage
|
||||
To apply a CTM delta, you pass it to the
|
||||
.Nm
|
||||
command.
|
||||
You can pass a CTM delta on stdin, or you can give the
|
||||
filename as an argument.
|
||||
If you do the latter, you make life a lot
|
||||
easier for your self, since the program can accept gzip'ed files and
|
||||
since it will not have to make a temporary copy of your file.
|
||||
You can
|
||||
specify multiple deltas at one time, they will be processed one at a
|
||||
time.
|
||||
Deltas that are already applied will be ignored.
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
command runs in a number of passes.
|
||||
It will process the entire
|
||||
input file in each pass, before commencing with the next pass.
|
||||
.Pp
|
||||
Before working on a file
|
||||
.Ar name
|
||||
.Nm
|
||||
first checks for the existence of the file
|
||||
.Ar name.ctm .
|
||||
If this file exists,
|
||||
.Nm
|
||||
works on it instead.
|
||||
.Pp
|
||||
Pass 1 will verify that the input file is OK.
|
||||
The syntax, the data
|
||||
and the global MD5 checksum will be checked.
|
||||
If any of these fail,
|
||||
.Nm
|
||||
will simply reject the input file.
|
||||
.Pp
|
||||
Pass 2 will validate that the directory tree is in the state expected by
|
||||
the CTM delta.
|
||||
This is done by looking for files and directories which
|
||||
should/should not exist and by checking the MD5 checksums of files.
|
||||
.Pp
|
||||
If a
|
||||
.Ar backup-file
|
||||
had been specified using the
|
||||
.Fl B
|
||||
option, all files that would be modified by this
|
||||
.Nm
|
||||
invocation are backed up
|
||||
to this file using the archiver command specified by the
|
||||
.Fl t
|
||||
option.
|
||||
The default archiver command is
|
||||
.Nm "tar -rf %s -T -" .
|
||||
.Pp
|
||||
Pass 3 will actually apply the delta.
|
||||
.Pp
|
||||
The list of files that would be modified by
|
||||
.Nm
|
||||
is subject to filtering regular expressions specified
|
||||
using the
|
||||
.Fl e
|
||||
and
|
||||
.Fl x
|
||||
options.
|
||||
The
|
||||
.Fl e
|
||||
and
|
||||
.Fl x
|
||||
options are applied in order of appearance on the command line.
|
||||
The last
|
||||
filter that matched a given file name determines whether the file would be
|
||||
operated on or left alone by
|
||||
.Nm .
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
utility
|
||||
will extract the file hierarchy below its working directory.
|
||||
Absolute
|
||||
filenames or filenames containing references through
|
||||
.Sq Pa .\&
|
||||
and
|
||||
.Sq Pa ..\&
|
||||
are explicitly prohibited as a security measure.
|
||||
.Ss Options
|
||||
.Bl -tag -width indent
|
||||
.It Fl b Ar basedir
|
||||
Prepend the path
|
||||
.Ar basedir
|
||||
to every filename.
|
||||
.It Fl B Ar backup-file
|
||||
Backup all files that would be modified by this CTM run to
|
||||
.Ar backup-file .
|
||||
If any filters are specified using the
|
||||
.Fl e
|
||||
and
|
||||
.Fl x
|
||||
options, then the final set of files backed up are those that would be
|
||||
modified by CTM after the filters are applied.
|
||||
.It Fl c
|
||||
Check it out, do not do anything.
|
||||
.It Fl e Ar regular_expression
|
||||
Match each name in the CTM file against
|
||||
.Ar regular_expression ,
|
||||
and if it matches process the file, otherwise leave it alone.
|
||||
There may be
|
||||
any number of these options.
|
||||
Use of this option disables the
|
||||
.Pa .ctm_status
|
||||
sequence number checks.
|
||||
For example, the expression
|
||||
.Ic ^usr.sbin/ctm
|
||||
for example, will select the
|
||||
.Pa usr.sbin/ctm
|
||||
source directory and all pathnames under it.
|
||||
.Pp
|
||||
Pathnames can be disabled from being considered by CTM using the
|
||||
.Fl x
|
||||
option.
|
||||
.It Fl F
|
||||
Force.
|
||||
.It Fl k
|
||||
Keep files and directories and do not remove them even if the CTM file
|
||||
specifies they are to be removed.
|
||||
If the
|
||||
.Fl B
|
||||
option is specified, these files and directories will not be backed up.
|
||||
.It Fl l
|
||||
List files that would be modified by this invocation of CTM and the
|
||||
actions that would be performed on them.
|
||||
Use of the
|
||||
.Fl l
|
||||
option disables the
|
||||
.Pa .ctm_status
|
||||
checks and integrity checks on the source tree being operated on.
|
||||
The
|
||||
.Fl l
|
||||
option can be combined with the
|
||||
.Fl e
|
||||
and
|
||||
.Fl x
|
||||
options to determine which files would be modified by the given set of
|
||||
command line options.
|
||||
.It Fl q
|
||||
Tell us less.
|
||||
.It Fl t Ar tar-command
|
||||
Use
|
||||
.Ar tar-command
|
||||
instead of the default archiver
|
||||
.Nm tar .
|
||||
This option takes effect only if a backup file had been specified using the
|
||||
.Fl B
|
||||
option.
|
||||
A %s in the tar command will be replaced by the name of the backup
|
||||
file.
|
||||
.It Fl T Ar tmpdir
|
||||
Put temporary files under
|
||||
.Ar tmpdir .
|
||||
.It Fl u
|
||||
Set modification time of created and modified files to the CTM delta
|
||||
creation time.
|
||||
.It Fl v
|
||||
Tell us more.
|
||||
.It Fl V Ar level
|
||||
Tell us more.
|
||||
.Ar Level
|
||||
is the level of verbosity.
|
||||
.It Fl x Ar regular_expression
|
||||
Match each name in the CTM file against
|
||||
.Ar regular_expression
|
||||
and if it matches, leave the file alone.
|
||||
There may be any number of these
|
||||
options.
|
||||
Use of this option disables the
|
||||
.Pa .ctm_status
|
||||
sequence number checks.
|
||||
.Pp
|
||||
Pathnames can be selected for CTM's consideration using the
|
||||
.Fl e
|
||||
option.
|
||||
.El
|
||||
.Sh SECURITY
|
||||
On its own, CTM is an insecure protocol
|
||||
- there is no authentication performed that the
|
||||
changes applied to the source code were sent by a
|
||||
trusted party, and so care should be taken if the
|
||||
CTM deltas are obtained via an unauthenticated
|
||||
medium such as regular email.
|
||||
It is a relatively simple matter for an attacker
|
||||
to forge a CTM delta to replace or precede the
|
||||
legitimate one and insert malicious code into your
|
||||
source tree.
|
||||
If the legitimate delta is somehow prevented from
|
||||
arriving, this will go unnoticed until a later
|
||||
delta attempts to touch the same file, at which
|
||||
point the MD5 checksum will fail.
|
||||
.Pp
|
||||
To remedy this insecurity, CTM pieces generated by
|
||||
FreeBSD.org are cryptographically signed in a
|
||||
format compatible with the GNU Privacy Guard
|
||||
utility, available in /usr/ports/security/gpg, and
|
||||
the Pretty Good Privacy v5 utility,
|
||||
/usr/ports/security/pgp5.
|
||||
The relevant public key can be obtained by
|
||||
fingering ctm@FreeBSD.org.
|
||||
.Pp
|
||||
CTM deltas which are thus signed cannot be
|
||||
undetectably altered by an attacker.
|
||||
Therefore it is recommended that you make use of
|
||||
GPG or PGP5 to verify the signatures if you
|
||||
receive your CTM deltas via email.
|
||||
.Sh ENVIRONMENT
|
||||
.Ev TMPDIR ,
|
||||
if set to a pathname, will cause ctm to use that pathname
|
||||
as the location of temporary file.
|
||||
See
|
||||
.Xr tempnam 3 ,
|
||||
for more details on this.
|
||||
The same effect may be achieved with the
|
||||
.Fl T
|
||||
flag.
|
||||
.Sh FILES
|
||||
.Pa .ctm_status
|
||||
contains the sequence number of the last CTM delta applied.
|
||||
Changing
|
||||
or removing this file will greatly confuse
|
||||
.Nm .
|
||||
.Pp
|
||||
Using the
|
||||
.Fl e
|
||||
and
|
||||
.Fl x
|
||||
options can update a partial subset of the source tree and causes sources
|
||||
to be in an inconsistent state.
|
||||
It is assumed that you know what you are
|
||||
doing when you use these options.
|
||||
.Sh EXAMPLES
|
||||
.Bd -literal
|
||||
cd ~cvs
|
||||
/usr/sbin/ctm ~ctm/cvs-*
|
||||
.Ed
|
||||
.Pp
|
||||
To extract and patch all sources under `lib'
|
||||
.Bd -literal
|
||||
cd ~/lib-srcs
|
||||
/usr/sbin/ctm -e '^lib' ~ctm/src-cur*
|
||||
.Ed
|
||||
.Sh DIAGNOSTICS
|
||||
Numerous messages, hopefully self-explanatory.
|
||||
The
|
||||
.Dq noise level
|
||||
can be adjusted with the
|
||||
.Fl q ,
|
||||
.Fl v
|
||||
and
|
||||
.Fl V
|
||||
options.
|
||||
.Sh SEE ALSO
|
||||
.Xr ctm_dequeue 1 ,
|
||||
.Xr ctm_rmail 1 ,
|
||||
.Xr ctm_smail 1 ,
|
||||
.Xr ctm 5
|
||||
.Rs
|
||||
.%T "Miscellaneous CTM on FreeBSD Resources"
|
||||
.%U http://ctm.berklix.org
|
||||
.Re
|
||||
.Sh HISTORY
|
||||
Initial trials were run during the work on
|
||||
.Fx 1.1.5 ,
|
||||
and many bugs and
|
||||
methods were hashed out.
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
command appeared in
|
||||
.Fx 2.1 .
|
||||
.Pp
|
||||
The latest
|
||||
.Nm
|
||||
code has been made available as a port (misc/ctm) in preparation of removal
|
||||
from base in
|
||||
.Fx 13.0 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The CTM system has been designed and implemented by
|
||||
.An Poul-Henning Kamp Aq Mt phk@FreeBSD.org .
|
||||
.Pp
|
||||
.An Joerg Wunsch Aq Mt joerg@FreeBSD.org
|
||||
wrote this man-page.
|
@ -1,182 +0,0 @@
|
||||
.\" ----------------------------------------------------------------------------
|
||||
.\" "THE BEER-WARE LICENSE" (Revision 42):
|
||||
.\" <joerg@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
.\" can do whatever you want with this stuff. If we meet some day, and you think
|
||||
.\" this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
.\" ----------------------------------------------------------------------------
|
||||
.\"
|
||||
.\" This manual page is partially obtained from Poul-Hennings CTM README
|
||||
.\" file.
|
||||
.\"
|
||||
.\" CTM and ctm(1) by <phk@FreeBSD.org>
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd March 25, 1995
|
||||
.Dt CTM 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm ctm
|
||||
.Nd source code mirror system
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
transfers data in a specific file format, called a CTM delta.
|
||||
.Pp
|
||||
CTM deltas consist of control lines and data chunks.
|
||||
Each control
|
||||
line starts with the letters
|
||||
.Dq CTM ,
|
||||
followed by a CTM statement and control data, and ends with a '\en'
|
||||
character.
|
||||
.Pp
|
||||
Data chunks always belong to the preceding control line, and the
|
||||
last field on that control line is the number of bytes in the data
|
||||
chunk.
|
||||
A trailing newline '\en' character follows each data chunk, this
|
||||
newline is not part of the chunk and is not included in the count.
|
||||
.Pp
|
||||
The CTM statements are as follows.
|
||||
.Bl -tag -width indent
|
||||
.It _BEGIN Ar version name number timestamp prefix
|
||||
This is the overall begin of a CTM delta file.
|
||||
The
|
||||
.Ar version
|
||||
field must match the program version
|
||||
(currently 2.0).
|
||||
.Ar Name
|
||||
is the name and
|
||||
.Ar number
|
||||
the sequence number of the CTM service, it is matched against the file
|
||||
.Pa .ctm_status
|
||||
to see if the delta has already been applied.
|
||||
.Ar Timestamp
|
||||
contains the year, month, day, hour, minute, and second of the
|
||||
time of delta creation for reference
|
||||
(followed by the letter
|
||||
.Sq Z
|
||||
meaning this is a UTC timestamp).
|
||||
The
|
||||
.Ar prefix
|
||||
field is currently not implemented.
|
||||
.It _END Ar md5
|
||||
This statement ends the CTM delta, the global
|
||||
.Ar md5
|
||||
checksum is matched against the MD5 checksum of the entire delta, up to
|
||||
and including the space (0x20) character following ``_END''.
|
||||
.It \&FM Ar name uid gid mode md5 count
|
||||
Make the file
|
||||
.Ar name ,
|
||||
the original file had the uid
|
||||
.Ar uid
|
||||
(numerical, decimal),
|
||||
the gid
|
||||
.Ar gid
|
||||
(numerical, decimal),
|
||||
mode
|
||||
.Ar mode
|
||||
(numerical, octal),
|
||||
and the MD5 checksum
|
||||
.Ar md5 .
|
||||
.Pp
|
||||
The following
|
||||
.Ar count
|
||||
bytes data are the contents of the new file.
|
||||
.It \&FS Ar name uid gid mode md5before md5after count
|
||||
Substitute the contents of file
|
||||
.Ar name ,
|
||||
the original file had the new uid
|
||||
.Ar uid
|
||||
(numerical, decimal),
|
||||
the new gid
|
||||
.Ar gid
|
||||
(numerical, decimal),
|
||||
new mode
|
||||
.Ar mode
|
||||
(numerical, octal),
|
||||
the old MD5 checksum
|
||||
.Ar md5before ,
|
||||
and the new MD5 checksum
|
||||
.Ar md5after .
|
||||
.Pp
|
||||
The following
|
||||
.Ar count
|
||||
bytes data are the contents of the new file.
|
||||
.Pp
|
||||
File substitution is used if the commands to edit a file would exceed
|
||||
the total file length, so substituting it is more efficient.
|
||||
.It \&FN Ar name uid gid mode md5before md5after count
|
||||
Edit the file
|
||||
.Ar name .
|
||||
The arguments are as above, but the data sections contains an
|
||||
.Xr diff 1
|
||||
-n script which should be applied to the file in question.
|
||||
.It \&FR Ar name md5
|
||||
Remove the file
|
||||
.Ar name ,
|
||||
which must match the MD5 checksum
|
||||
.Ar md5 .
|
||||
.It \&AS Ar name uid gid mode
|
||||
The original file
|
||||
.Ar name
|
||||
changed its owner to
|
||||
.Ar uid ,
|
||||
its group to
|
||||
.Ar gid ,
|
||||
and/or its mode to
|
||||
.Ar mode .
|
||||
.It \&DM Ar name uid gid mode
|
||||
The directory
|
||||
.Ar name
|
||||
is to be created, it had originally the owner
|
||||
.Ar uid ,
|
||||
group
|
||||
.Ar gid ,
|
||||
and mode
|
||||
.Ar mode .
|
||||
.It \&DR Ar name
|
||||
The directory
|
||||
.Ar name
|
||||
is to be removed.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
In the following example, long lines have been folded to make them
|
||||
printable
|
||||
(marked by backslashes).
|
||||
.Bd -literal
|
||||
CTM_BEGIN 2.0 cvs-cur 485 19950324214652Z .
|
||||
CTMFR src/sys/gnu/i386/isa/scd.c,v 5225f13aa3c7e458f9dd0d4bb637b18d
|
||||
CTMFR src/sys/gnu/i386/isa/scdreg.h,v e5af42b8a06f2c8030b93a7d71afb223
|
||||
CTMDM src/sys/gnu/i386/isa/Attic 0 552 775
|
||||
CTMFS .ctm_status 545 552 664 d9ccd2a84a9dbb8db56ba85663adebf0 \\
|
||||
e2a10c6f66428981782a0a18a789ee2e 12
|
||||
cvs-cur 485
|
||||
|
||||
CTMFN CVSROOT/commitlogs/gnu 545 552 664 \\
|
||||
5d7bc3549140d860bd9641b5782c002d 7fb04ed84b48160c9b8eea84b4c0b6e3 394
|
||||
a6936 21
|
||||
ache 95/03/24 09:59:50
|
||||
|
||||
Modified: gnu/lib/libdialog kernel.c prgbox.c
|
||||
Log:
|
||||
[...]
|
||||
CTM_END 74ddd298d76215ae45a077a4b6a74e9c
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr ctm 1 ,
|
||||
.Xr ctm_rmail 1 ,
|
||||
.Xr ed 1
|
||||
.Sh HISTORY
|
||||
Initial trials ran during the
|
||||
.Fx 1.1.5 ,
|
||||
and many bugs and
|
||||
methods were hashed out.
|
||||
The CTM system has been made publicly available in
|
||||
.Fx 2.1 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The CTM system has been designed and implemented by
|
||||
.An Poul-Henning Kamp Aq Mt phk@FreeBSD.org .
|
||||
.Pp
|
||||
.An Joerg Wunsch Aq Mt joerg@FreeBSD.org
|
||||
wrote this man-page.
|
@ -1,336 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: Beerware
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
* This is the client program of 'CTM'. It will apply a CTM-patch to a
|
||||
* collection of files.
|
||||
*
|
||||
* Options we'd like to see:
|
||||
*
|
||||
* -a Attempt best effort.
|
||||
* -d <int> Debug TBD.
|
||||
* -m <mail-addr> Email me instead.
|
||||
* -r <name> Reconstruct file.
|
||||
* -R <file> Read list of files to reconstruct.
|
||||
*
|
||||
* Options we have:
|
||||
* -b <dir> Base-dir
|
||||
* -B <file> Backup to tar-file.
|
||||
* -t Tar command (default as in TARCMD).
|
||||
* -c Check it out, don't do anything.
|
||||
* -F Force
|
||||
* -q Tell us less.
|
||||
* -T <tmpdir>. Temporary files.
|
||||
* -u Set all file modification times to the timestamp
|
||||
* -v Tell us more.
|
||||
* -V <level> Tell us more level = number of -v
|
||||
* -k Keep files and directories that would have been removed.
|
||||
* -l List actions.
|
||||
*
|
||||
* Options we don't actually use:
|
||||
* -p Less paranoid.
|
||||
* -P Paranoid.
|
||||
*/
|
||||
|
||||
#define EXTERN /* */
|
||||
#include <paths.h>
|
||||
#include "ctm.h"
|
||||
|
||||
#define CTM_STATUS ".ctm_status"
|
||||
|
||||
extern int Proc(char *, unsigned applied);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int stat=0, err=0;
|
||||
int c;
|
||||
unsigned applied = 0;
|
||||
FILE *statfile;
|
||||
struct CTM_Filter *nfilter = NULL; /* new filter */
|
||||
u_char * basedir;
|
||||
|
||||
basedir = NULL;
|
||||
Verbose = 1;
|
||||
Paranoid = 1;
|
||||
SetTime = 0;
|
||||
KeepIt = 0;
|
||||
ListIt = 0;
|
||||
BackupFile = NULL;
|
||||
TarCmd = TARCMD;
|
||||
LastFilter = FilterList = NULL;
|
||||
TmpDir = getenv("TMPDIR");
|
||||
if (TmpDir == NULL)
|
||||
TmpDir = strdup(_PATH_TMP);
|
||||
setbuf(stderr,0);
|
||||
setbuf(stdout,0);
|
||||
|
||||
while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) {
|
||||
switch (c) {
|
||||
case 'b': basedir = optarg; break; /* Base Directory */
|
||||
case 'B': BackupFile = optarg; break;
|
||||
case 'c': CheckIt++; break; /* Only check it */
|
||||
case 'F': Force = 1; break;
|
||||
case 'k': KeepIt++; break; /* Don't do removes */
|
||||
case 'l': ListIt++; break; /* Only list actions and files */
|
||||
case 'p': Paranoid--; break; /* Less Paranoid */
|
||||
case 'P': Paranoid++; break; /* More Paranoid */
|
||||
case 'q': Verbose--; break; /* Quiet */
|
||||
case 't': TarCmd = optarg; break; /* archiver command */
|
||||
case 'T': TmpDir = optarg; break; /* set temporary directory */
|
||||
case 'u': SetTime++; break; /* Set timestamp on files */
|
||||
case 'v': Verbose++; break; /* Verbose */
|
||||
case 'V': sscanf(optarg,"%d", &c); /* Verbose */
|
||||
Verbose += c;
|
||||
break;
|
||||
case 'e': /* filter expressions */
|
||||
case 'x':
|
||||
if (NULL == (nfilter = Malloc(sizeof(struct CTM_Filter)))) {
|
||||
warnx("out of memory for expressions: \"%s\"", optarg);
|
||||
stat++;
|
||||
break;
|
||||
}
|
||||
|
||||
(void) memset(nfilter, 0, sizeof(struct CTM_Filter));
|
||||
|
||||
if (0 != (err =
|
||||
regcomp(&nfilter->CompiledRegex, optarg, REG_NOSUB))) {
|
||||
|
||||
char errmsg[128];
|
||||
|
||||
regerror(err, &nfilter->CompiledRegex, errmsg,
|
||||
sizeof(errmsg));
|
||||
warnx("regular expression: \"%s\"", errmsg);
|
||||
stat++;
|
||||
break;
|
||||
}
|
||||
|
||||
/* note whether the filter enables or disables on match */
|
||||
nfilter->Action =
|
||||
(('e' == c) ? CTM_FILTER_ENABLE : CTM_FILTER_DISABLE);
|
||||
|
||||
/* link in the expression into the list */
|
||||
nfilter->Next = NULL;
|
||||
if (NULL == FilterList) {
|
||||
LastFilter = FilterList = nfilter; /* init head and tail */
|
||||
} else { /* place at tail */
|
||||
LastFilter->Next = nfilter;
|
||||
LastFilter = nfilter;
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
warnx("option '%c' requires an argument",optopt);
|
||||
stat++;
|
||||
break;
|
||||
case '?':
|
||||
warnx("option '%c' not supported",optopt);
|
||||
stat++;
|
||||
break;
|
||||
default:
|
||||
warnx("option '%c' not yet implemented",optopt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(stat) {
|
||||
warnx("%d errors during option processing",stat);
|
||||
return Exit_Pilot;
|
||||
}
|
||||
fprintf(stderr, "CTM will be removed from FreeBSD-13, and will be "
|
||||
"provided as a port (misc/ctm) or package (ctm).\n\n");
|
||||
|
||||
stat = Exit_Done;
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (basedir == NULL) {
|
||||
Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1);
|
||||
CatPtr = Buffer;
|
||||
*Buffer = '\0';
|
||||
} else {
|
||||
Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1);
|
||||
strcpy(Buffer, basedir);
|
||||
CatPtr = Buffer + strlen(basedir);
|
||||
if (CatPtr[-1] != '/') {
|
||||
strcat(Buffer, "/");
|
||||
CatPtr++;
|
||||
}
|
||||
}
|
||||
strcat(Buffer, CTM_STATUS);
|
||||
|
||||
if(ListIt)
|
||||
applied = 0;
|
||||
else
|
||||
if((statfile = fopen(Buffer, "r")) == NULL) {
|
||||
if (Verbose > 0)
|
||||
warnx("warning: %s not found", Buffer);
|
||||
} else {
|
||||
fscanf(statfile, "%*s %u", &applied);
|
||||
fclose(statfile);
|
||||
}
|
||||
|
||||
if(!argc)
|
||||
stat |= Proc("-", applied);
|
||||
|
||||
while(argc-- && stat == Exit_Done) {
|
||||
stat |= Proc(*argv++, applied);
|
||||
stat &= ~(Exit_Version | Exit_NoMatch);
|
||||
}
|
||||
|
||||
if(stat == Exit_Done)
|
||||
stat = Exit_OK;
|
||||
|
||||
if(Verbose > 0)
|
||||
warnx("exit(%d)",stat);
|
||||
|
||||
if (FilterList)
|
||||
for (nfilter = FilterList; nfilter; ) {
|
||||
struct CTM_Filter *tmp = nfilter->Next;
|
||||
Free(nfilter);
|
||||
nfilter = tmp;
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int
|
||||
Proc(char *filename, unsigned applied)
|
||||
{
|
||||
FILE *f;
|
||||
int i;
|
||||
char *p = strrchr(filename,'.');
|
||||
|
||||
if(!strcmp(filename,"-")) {
|
||||
p = 0;
|
||||
f = stdin;
|
||||
} else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) {
|
||||
p = alloca(20 + strlen(filename));
|
||||
strcpy(p,"gunzip < ");
|
||||
strcat(p,filename);
|
||||
f = popen(p,"r");
|
||||
if(!f) { warn("%s", p); return Exit_Garbage; }
|
||||
} else {
|
||||
p = 0;
|
||||
f = fopen(filename,"r");
|
||||
}
|
||||
if(!f) {
|
||||
warn("%s", filename);
|
||||
return Exit_Garbage;
|
||||
}
|
||||
|
||||
if(Verbose > 1)
|
||||
fprintf(stderr,"Working on <%s>\n",filename);
|
||||
|
||||
Delete(FileName);
|
||||
FileName = String(filename);
|
||||
|
||||
/* If we cannot seek, we're doomed, so copy to a tmp-file in that case */
|
||||
if(!p && -1 == fseek(f,0,SEEK_END)) {
|
||||
char *fn;
|
||||
FILE *f2;
|
||||
int fd;
|
||||
|
||||
if (asprintf(&fn, "%s/CTMclient.XXXXXXXXXX", TmpDir) == -1) {
|
||||
fprintf(stderr, "Cannot allocate memory\n");
|
||||
fclose(f);
|
||||
return Exit_Broke;
|
||||
}
|
||||
if ((fd = mkstemp(fn)) == -1 || (f2 = fdopen(fd, "w+")) == NULL) {
|
||||
warn("%s", fn);
|
||||
free(fn);
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
fclose(f);
|
||||
return Exit_Broke;
|
||||
}
|
||||
unlink(fn);
|
||||
if (Verbose > 0)
|
||||
fprintf(stderr,"Writing tmp-file \"%s\"\n",fn);
|
||||
free(fn);
|
||||
while(EOF != (i=getc(f)))
|
||||
if(EOF == putc(i,f2)) {
|
||||
fclose(f2);
|
||||
return Exit_Broke;
|
||||
}
|
||||
fclose(f);
|
||||
f = f2;
|
||||
}
|
||||
|
||||
if(!p)
|
||||
rewind(f);
|
||||
|
||||
if((i=Pass1(f, applied)))
|
||||
goto exit_and_close;
|
||||
|
||||
if(ListIt) {
|
||||
i = Exit_Done;
|
||||
goto exit_and_close;
|
||||
}
|
||||
|
||||
if(!p) {
|
||||
rewind(f);
|
||||
} else {
|
||||
pclose(f);
|
||||
f = popen(p,"r");
|
||||
if(!f) { warn("%s", p); return Exit_Broke; }
|
||||
}
|
||||
|
||||
i=Pass2(f);
|
||||
|
||||
if(!p) {
|
||||
rewind(f);
|
||||
} else {
|
||||
pclose(f);
|
||||
f = popen(p,"r");
|
||||
if(!f) { warn("%s", p); return Exit_Broke; }
|
||||
}
|
||||
|
||||
if(i) {
|
||||
if((!Force) || (i & ~Exit_Forcible))
|
||||
goto exit_and_close;
|
||||
}
|
||||
|
||||
if(CheckIt) {
|
||||
if (Verbose > 0)
|
||||
fprintf(stderr,"All checks out ok.\n");
|
||||
i = Exit_Done;
|
||||
goto exit_and_close;
|
||||
}
|
||||
|
||||
/* backup files if requested */
|
||||
if(BackupFile) {
|
||||
|
||||
i = PassB(f);
|
||||
|
||||
if(!p) {
|
||||
rewind(f);
|
||||
} else {
|
||||
pclose(f);
|
||||
f = popen(p,"r");
|
||||
if(!f) { warn("%s", p); return Exit_Broke; }
|
||||
}
|
||||
}
|
||||
|
||||
i=Pass3(f);
|
||||
|
||||
exit_and_close:
|
||||
if(!p)
|
||||
fclose(f);
|
||||
else
|
||||
pclose(f);
|
||||
|
||||
if(i)
|
||||
return i;
|
||||
|
||||
if (Verbose > 0)
|
||||
fprintf(stderr,"All done ok\n");
|
||||
|
||||
return Exit_Done;
|
||||
}
|
@ -1,163 +0,0 @@
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <md5.h>
|
||||
#include <regex.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define VERSION "2.0"
|
||||
|
||||
#define SUBSUFF ".ctm"
|
||||
#define TMPSUFF ".ctmtmp"
|
||||
#define TARCMD "tar -rf %s -T -"
|
||||
|
||||
/* The fields... */
|
||||
#define CTM_F_MASK 0xff
|
||||
#define CTM_F_Name 0x01
|
||||
#define CTM_F_Uid 0x02
|
||||
#define CTM_F_Gid 0x03
|
||||
#define CTM_F_Mode 0x04
|
||||
#define CTM_F_MD5 0x05
|
||||
#define CTM_F_Count 0x06
|
||||
#define CTM_F_Bytes 0x07
|
||||
|
||||
/* The qualifiers... */
|
||||
#define CTM_Q_MASK 0xff00
|
||||
#define CTM_Q_Name_File 0x0100
|
||||
#define CTM_Q_Name_Dir 0x0200
|
||||
#define CTM_Q_Name_New 0x0400
|
||||
#define CTM_Q_Name_Subst 0x0800
|
||||
#define CTM_Q_MD5_After 0x0100
|
||||
#define CTM_Q_MD5_Before 0x0200
|
||||
#define CTM_Q_MD5_Chunk 0x0400
|
||||
#define CTM_Q_MD5_Force 0x0800
|
||||
|
||||
struct CTM_Syntax {
|
||||
char *Key; /* CTM key for operation */
|
||||
int *List; /* List of operations */
|
||||
};
|
||||
|
||||
extern struct CTM_Syntax Syntax[];
|
||||
|
||||
struct CTM_Filter {
|
||||
struct CTM_Filter *Next; /* next filter in the list */
|
||||
int Action; /* enable or disable */
|
||||
regex_t CompiledRegex; /* compiled regex */
|
||||
};
|
||||
|
||||
#define CTM_FILTER_DISABLE 0
|
||||
#define CTM_FILTER_ENABLE 1
|
||||
|
||||
#define Malloc malloc
|
||||
#define Free free
|
||||
#define Delete(foo) if (!foo) ; else {Free(foo); foo = 0; }
|
||||
#define String(foo) strdup(foo)
|
||||
|
||||
#ifndef EXTERN
|
||||
# define EXTERN extern
|
||||
#endif
|
||||
EXTERN u_char *Version;
|
||||
EXTERN u_char *Name;
|
||||
EXTERN u_char *Nbr;
|
||||
EXTERN u_char *TimeStamp;
|
||||
EXTERN u_char *Prefix;
|
||||
EXTERN u_char *FileName;
|
||||
EXTERN u_char *TmpDir;
|
||||
EXTERN u_char *CatPtr;
|
||||
EXTERN u_char *Buffer;
|
||||
EXTERN u_char *BackupFile;
|
||||
EXTERN u_char *TarCmd;
|
||||
|
||||
/*
|
||||
* Paranoid -- Just in case they should be after us...
|
||||
* 0 not at all.
|
||||
* 1 normal.
|
||||
* 2 somewhat.
|
||||
* 3 you bet!.
|
||||
*
|
||||
* Verbose -- What to tell mom...
|
||||
* 0 Nothing which wouldn't surprise.
|
||||
* 1 Normal.
|
||||
* 2 Show progress '.'.
|
||||
* 3 Show progress names, and actions.
|
||||
* 4 even more...
|
||||
* and so on
|
||||
*
|
||||
* ExitCode -- our Epitaph
|
||||
* 0 Perfect, all input digested, no problems
|
||||
* 1 Bad input, no point in retrying.
|
||||
* 2 Pilot error, commandline problem &c
|
||||
* 4 Out of resources.
|
||||
* 8 Destination-tree not correct.
|
||||
* 16 Destination-tree not correct, can force.
|
||||
* 32 Internal problems.
|
||||
*
|
||||
*/
|
||||
|
||||
EXTERN int Paranoid;
|
||||
EXTERN int Verbose;
|
||||
EXTERN int Exit;
|
||||
EXTERN int Force;
|
||||
EXTERN int CheckIt;
|
||||
EXTERN int KeepIt;
|
||||
EXTERN int ListIt;
|
||||
EXTERN int SetTime;
|
||||
EXTERN struct timeval Times[2];
|
||||
EXTERN struct CTM_Filter *FilterList;
|
||||
EXTERN struct CTM_Filter *LastFilter;
|
||||
|
||||
#define Exit_OK 0
|
||||
#define Exit_Garbage 1
|
||||
#define Exit_Pilot 2
|
||||
#define Exit_Broke 4
|
||||
#define Exit_NotOK 8
|
||||
#define Exit_Forcible 16
|
||||
#define Exit_Mess 32
|
||||
#define Exit_Done 64
|
||||
#define Exit_Version 128
|
||||
#define Exit_NoMatch 256
|
||||
|
||||
void Fatal_(int ln, char *fn, char *kind);
|
||||
#define Fatal(foo) Fatal_(__LINE__,__FILE__,foo)
|
||||
#define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.")
|
||||
#define WRONG {Assert(); return Exit_Mess;}
|
||||
|
||||
u_char * Ffield(FILE *fd, MD5_CTX *ctx,u_char term);
|
||||
u_char * Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose);
|
||||
|
||||
int Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term);
|
||||
|
||||
u_char * Fdata(FILE *fd, int u_chars, MD5_CTX *ctx);
|
||||
|
||||
#define GETFIELD(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD
|
||||
#define GETFIELDCOPY(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD; else p=String(p)
|
||||
#define GETBYTECNT(p,q) if(0 >((p)= Fbytecnt(fd,&ctx,(q)))) return BADREAD
|
||||
#define GETDATA(p,q) if(!((p) = Fdata(fd,(q),&ctx))) return BADREAD
|
||||
#define GETNAMECOPY(p,q,r,v) if(!((p)=Fname(fd,&ctx,(q),(r),(v)))) return BADREAD; else p=String(p)
|
||||
|
||||
int Pass1(FILE *fd, unsigned applied);
|
||||
int Pass2(FILE *fd);
|
||||
int PassB(FILE *fd);
|
||||
int Pass3(FILE *fd);
|
||||
|
||||
int ctm_edit(u_char *script, int length, char *filein, char *fileout);
|
@ -1,116 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: Beerware
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctm.h"
|
||||
|
||||
int
|
||||
ctm_edit(u_char *script, int length, char *filein, char *fileout)
|
||||
{
|
||||
u_char *ep, cmd;
|
||||
int ln, ln2, iln, ret=0, c;
|
||||
FILE *fi=0,*fo=0;
|
||||
|
||||
fi = fopen(filein,"r");
|
||||
if(!fi) {
|
||||
warn("%s", filein);
|
||||
return 8;
|
||||
}
|
||||
|
||||
fo = fopen(fileout,"w");
|
||||
if(!fo) {
|
||||
warn("%s", fileout);
|
||||
fclose(fi);
|
||||
return 4;
|
||||
}
|
||||
iln = 1;
|
||||
for(ep=script;ep < script+length;) {
|
||||
cmd = *ep++;
|
||||
if(cmd != 'a' && cmd != 'd') { ret = 1; goto bye; }
|
||||
ln = 0;
|
||||
while(isdigit(*ep)) {
|
||||
ln *= 10;
|
||||
ln += (*ep++ - '0');
|
||||
}
|
||||
if(*ep++ != ' ') { ret = 1; goto bye; }
|
||||
ln2 = 0;
|
||||
while(isdigit(*ep)) {
|
||||
ln2 *= 10;
|
||||
ln2 += (*ep++ - '0');
|
||||
}
|
||||
if(*ep++ != '\n') { ret = 1; goto bye; }
|
||||
|
||||
|
||||
if(cmd == 'd') {
|
||||
while(iln < ln) {
|
||||
c = getc(fi);
|
||||
if(c == EOF) { ret = 1; goto bye; }
|
||||
putc(c,fo);
|
||||
if(c == '\n')
|
||||
iln++;
|
||||
}
|
||||
while(ln2) {
|
||||
c = getc(fi);
|
||||
if(c == EOF) { ret = 1; goto bye; }
|
||||
if(c != '\n')
|
||||
continue;
|
||||
ln2--;
|
||||
iln++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(cmd == 'a') {
|
||||
while(iln <= ln) {
|
||||
c = getc(fi);
|
||||
if(c == EOF) { ret = 1; goto bye; }
|
||||
putc(c,fo);
|
||||
if(c == '\n')
|
||||
iln++;
|
||||
}
|
||||
while(ln2) {
|
||||
c = *ep++;
|
||||
putc(c,fo);
|
||||
if(c != '\n')
|
||||
continue;
|
||||
ln2--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ret = 1;
|
||||
goto bye;
|
||||
}
|
||||
while(1) {
|
||||
c = getc(fi);
|
||||
if(c == EOF) break;
|
||||
putc(c,fo);
|
||||
}
|
||||
ret = 0;
|
||||
bye:
|
||||
if(fi) {
|
||||
if(fclose(fi) != 0) {
|
||||
warn("%s", filein);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
if(fo) {
|
||||
if(fflush(fo) != 0) {
|
||||
warn("%s", fileout);
|
||||
ret = 1;
|
||||
}
|
||||
if(fclose(fo) != 0) {
|
||||
warn("%s", fileout);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: Beerware
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctm.h"
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
Fatal_(int ln, char *fn, char *kind)
|
||||
{
|
||||
if(Verbose > 2)
|
||||
fprintf(stderr,"Fatal error. (%s:%d)\n",fn,ln);
|
||||
fprintf(stderr,"%s Fatal error: %s\n",FileName, kind);
|
||||
}
|
||||
#define Fatal(foo) Fatal_(__LINE__,__FILE__,foo)
|
||||
#define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.")
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* get next field, check that the terminating whitespace is what we expect */
|
||||
u_char *
|
||||
Ffield(FILE *fd, MD5_CTX *ctx,u_char term)
|
||||
{
|
||||
static u_char buf[BUFSIZ];
|
||||
int i,l;
|
||||
|
||||
for(l=0;;) {
|
||||
if((i=getc(fd)) == EOF) {
|
||||
Fatal("Truncated patch.");
|
||||
return 0;
|
||||
}
|
||||
buf[l++] = i;
|
||||
if(isspace(i))
|
||||
break;
|
||||
if(l >= sizeof buf) {
|
||||
Fatal("Corrupt patch.");
|
||||
printf("Token is too long.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
buf[l] = '\0';
|
||||
MD5Update(ctx,buf,l);
|
||||
if(buf[l-1] != term) {
|
||||
Fatal("Corrupt patch.");
|
||||
fprintf(stderr,"Expected \"%s\" but didn't find it {%02x}.\n",
|
||||
term == '\n' ? "\\n" : " ",buf[l-1]);
|
||||
if(Verbose > 4)
|
||||
fprintf(stderr,"{%s}\n",buf);
|
||||
return 0;
|
||||
}
|
||||
buf[--l] = '\0';
|
||||
if(Verbose > 4)
|
||||
fprintf(stderr,"<%s>\n",buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int
|
||||
Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term)
|
||||
{
|
||||
u_char *p,*q;
|
||||
int u_chars=0;
|
||||
|
||||
p = Ffield(fd,ctx,term);
|
||||
if(!p) return -1;
|
||||
for(q=p;*q;q++) {
|
||||
if(!isdigit(*q)) {
|
||||
Fatal("Bytecount contains non-digit.");
|
||||
return -1;
|
||||
}
|
||||
u_chars *= 10;
|
||||
u_chars += (*q - '0');
|
||||
}
|
||||
return u_chars;
|
||||
}
|
||||
|
||||
u_char *
|
||||
Fdata(FILE *fd, int u_chars, MD5_CTX *ctx)
|
||||
{
|
||||
u_char *p = Malloc(u_chars+1);
|
||||
|
||||
if(u_chars+1 != fread(p,1,u_chars+1,fd)) {
|
||||
Fatal("Truncated patch.");
|
||||
return 0;
|
||||
}
|
||||
MD5Update(ctx,p,u_chars+1);
|
||||
if(p[u_chars] != '\n') {
|
||||
if(Verbose > 3)
|
||||
printf("FileData wasn't followed by a newline.\n");
|
||||
Fatal("Corrupt patch.");
|
||||
return 0;
|
||||
}
|
||||
p[u_chars] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* get the filename in the next field, prepend BaseDir and give back the result
|
||||
strings. The sustitute filename is return (the one with the suffix SUBSUFF)
|
||||
if it exists and the qualifier contains CTM_Q_Name_Subst
|
||||
NOTA: Buffer is already initialize with BaseDir, CatPtr is the insertion
|
||||
point on this buffer + the length test in Ffield() is enough for Fname() */
|
||||
|
||||
u_char *
|
||||
Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose)
|
||||
{
|
||||
u_char * p;
|
||||
struct stat st;
|
||||
|
||||
if ((p = Ffield(fd,ctx,term)) == NULL) return(NULL);
|
||||
|
||||
strcpy(CatPtr, p);
|
||||
|
||||
if (!(qual & CTM_Q_Name_Subst)) return(Buffer);
|
||||
|
||||
p = Buffer + strlen(Buffer);
|
||||
|
||||
strcat(Buffer, SUBSUFF);
|
||||
|
||||
if ( -1 == stat(Buffer, &st) ) {
|
||||
*p = '\0';
|
||||
} else {
|
||||
if(verbose > 2)
|
||||
fprintf(stderr,"Using %s as substitute file\n", Buffer);
|
||||
}
|
||||
|
||||
return (Buffer);
|
||||
}
|
@ -1,254 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: Beerware
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctm.h"
|
||||
#define BADREAD 1
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Pass1 -- Validate the incoming CTM-file.
|
||||
*/
|
||||
|
||||
int
|
||||
Pass1(FILE *fd, unsigned applied)
|
||||
{
|
||||
u_char *p,*q;
|
||||
MD5_CTX ctx;
|
||||
int i,j,sep,cnt;
|
||||
u_char *md5=0,*name=0,*trash=0;
|
||||
struct CTM_Syntax *sp;
|
||||
int slashwarn=0, match=0, total_matches=0;
|
||||
unsigned current;
|
||||
char md5_1[33];
|
||||
|
||||
if(Verbose>3)
|
||||
printf("Pass1 -- Checking integrity of incoming CTM-patch\n");
|
||||
MD5Init (&ctx);
|
||||
|
||||
GETFIELD(p,' '); /* CTM_BEGIN */
|
||||
if(strcmp(p,"CTM_BEGIN")) {
|
||||
Fatal("Probably not a CTM-patch at all.");
|
||||
if(Verbose>3)
|
||||
fprintf(stderr,"Expected \"CTM_BEGIN\" got \"%s\".\n",p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
GETFIELDCOPY(Version,' '); /* <Version> */
|
||||
if(strcmp(Version,VERSION)) {
|
||||
Fatal("CTM-patch is wrong version.");
|
||||
if(Verbose>3)
|
||||
fprintf(stderr,"Expected \"%s\" got \"%s\".\n",VERSION,p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
GETFIELDCOPY(Name,' '); /* <Name> */
|
||||
GETFIELDCOPY(Nbr,' '); /* <Nbr> */
|
||||
GETFIELDCOPY(TimeStamp,' '); /* <TimeStamp> */
|
||||
GETFIELDCOPY(Prefix,'\n'); /* <Prefix> */
|
||||
|
||||
sscanf(Nbr, "%u", ¤t);
|
||||
if (FilterList || ListIt)
|
||||
current = 0; /* ignore if -l or if filters are present */
|
||||
if(current && current <= applied) {
|
||||
if(Verbose > 0)
|
||||
fprintf(stderr,"Delta number %u is already applied; ignoring.\n",
|
||||
current);
|
||||
return Exit_Version;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
Delete(md5);
|
||||
Delete(name);
|
||||
Delete(trash);
|
||||
cnt = -1;
|
||||
/* if a filter list is defined we assume that all pathnames require
|
||||
an action opposite to that requested by the first filter in the
|
||||
list.
|
||||
If no filter is defined, all pathnames are assumed to match. */
|
||||
match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
|
||||
|
||||
GETFIELD(p,' '); /* CTM_something */
|
||||
|
||||
if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') {
|
||||
Fatal("Expected CTM keyword.");
|
||||
fprintf(stderr,"Got [%s]\n",p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!strcmp(p+3,"_END"))
|
||||
break;
|
||||
|
||||
for(sp=Syntax;sp->Key;sp++)
|
||||
if(!strcmp(p+3,sp->Key))
|
||||
goto found;
|
||||
Fatal("Expected CTM keyword.");
|
||||
fprintf(stderr,"Got [%s]\n",p);
|
||||
return 1;
|
||||
found:
|
||||
if(Verbose > 5)
|
||||
fprintf(stderr,"%s ",sp->Key);
|
||||
for(i=0;(j = sp->List[i]);i++) {
|
||||
if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
|
||||
sep = ' ';
|
||||
else
|
||||
sep = '\n';
|
||||
|
||||
if(Verbose > 5)
|
||||
fprintf(stderr," %x(%d)",sp->List[i],sep);
|
||||
|
||||
switch (j & CTM_F_MASK) {
|
||||
case CTM_F_Name: /* XXX check for garbage and .. */
|
||||
GETFIELDCOPY(name,sep);
|
||||
j = strlen(name);
|
||||
if(name[j-1] == '/' && !slashwarn) {
|
||||
fprintf(stderr,"Warning: contains trailing slash\n");
|
||||
slashwarn++;
|
||||
}
|
||||
if (name[0] == '/') {
|
||||
Fatal("Absolute paths are illegal.");
|
||||
Delete(name);
|
||||
return Exit_Mess;
|
||||
}
|
||||
q = name;
|
||||
for (;;) {
|
||||
if (q[0] == '.' && q[1] == '.')
|
||||
if (q[2] == '/' || q[2] == '\0') {
|
||||
Fatal("Paths containing '..' are illegal.");
|
||||
Delete(name);
|
||||
return Exit_Mess;
|
||||
}
|
||||
if ((q = strchr(q, '/')) == NULL)
|
||||
break;
|
||||
q++;
|
||||
}
|
||||
|
||||
/* if we have been asked to `keep' files then skip
|
||||
removes; i.e. we don't match these entries at
|
||||
all. */
|
||||
if (KeepIt &&
|
||||
(!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) {
|
||||
match = CTM_FILTER_DISABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If filter expression have been defined, match the
|
||||
path name against the expression list. */
|
||||
|
||||
if (FilterList) {
|
||||
struct CTM_Filter *filter;
|
||||
|
||||
for (filter = FilterList; filter;
|
||||
filter = filter->Next) {
|
||||
if (0 == regexec(&filter->CompiledRegex, name,
|
||||
0, 0, 0))
|
||||
/* if the name matches, adopt the
|
||||
action */
|
||||
match = filter->Action;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add up the total number of matches */
|
||||
total_matches += match;
|
||||
break;
|
||||
case CTM_F_Uid:
|
||||
GETFIELD(p,sep);
|
||||
while(*p) {
|
||||
if(!isdigit(*p)) {
|
||||
Fatal("Non-digit in uid.");
|
||||
return 32;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
case CTM_F_Gid:
|
||||
GETFIELD(p,sep);
|
||||
while(*p) {
|
||||
if(!isdigit(*p)) {
|
||||
Fatal("Non-digit in gid.");
|
||||
return 32;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
case CTM_F_Mode:
|
||||
GETFIELD(p,sep);
|
||||
while(*p) {
|
||||
if(!isdigit(*p)) {
|
||||
Fatal("Non-digit in mode.");
|
||||
return 32;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
case CTM_F_MD5:
|
||||
if(j & CTM_Q_MD5_Chunk) {
|
||||
GETFIELDCOPY(md5,sep); /* XXX check for garbage */
|
||||
} else if(j & CTM_Q_MD5_Before) {
|
||||
GETFIELD(p,sep); /* XXX check for garbage */
|
||||
} else if(j & CTM_Q_MD5_After) {
|
||||
GETFIELD(p,sep); /* XXX check for garbage */
|
||||
} else {
|
||||
fprintf(stderr,"List = 0x%x\n",j);
|
||||
Fatal("Unqualified MD5.");
|
||||
return 32;
|
||||
}
|
||||
break;
|
||||
case CTM_F_Count:
|
||||
GETBYTECNT(cnt,sep);
|
||||
break;
|
||||
case CTM_F_Bytes:
|
||||
if(cnt < 0) WRONG
|
||||
GETDATA(trash,cnt);
|
||||
p = MD5Data(trash,cnt,md5_1);
|
||||
if(md5 && strcmp(md5,p)) {
|
||||
Fatal("Internal MD5 failed.");
|
||||
return Exit_Garbage;
|
||||
default:
|
||||
fprintf(stderr,"List = 0x%x\n",j);
|
||||
Fatal("List had garbage.");
|
||||
return Exit_Garbage;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(Verbose > 5)
|
||||
putc('\n',stderr);
|
||||
if(ListIt && match)
|
||||
printf("> %s %s\n", sp->Key, name);
|
||||
}
|
||||
|
||||
Delete(md5);
|
||||
Delete(name);
|
||||
Delete(trash);
|
||||
|
||||
q = MD5End (&ctx,md5_1);
|
||||
if(Verbose > 2)
|
||||
printf("Expecting Global MD5 <%s>\n",q);
|
||||
GETFIELD(p,'\n'); /* <MD5> */
|
||||
if(Verbose > 2)
|
||||
printf("Reference Global MD5 <%s>\n",p);
|
||||
if(strcmp(q,p)) {
|
||||
Fatal("MD5 sum doesn't match.");
|
||||
fprintf(stderr,"\tI have:<%s>\n",q);
|
||||
fprintf(stderr,"\tShould have been:<%s>\n",p);
|
||||
return Exit_Garbage;
|
||||
}
|
||||
if (-1 != getc(fd)) {
|
||||
if(!Force) {
|
||||
Fatal("Trailing junk in CTM-file. Can Force with -F.");
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
if ((Verbose > 1) && (0 == total_matches))
|
||||
printf("No matches in \"%s\"\n", FileName);
|
||||
return (total_matches ? Exit_OK : Exit_NoMatch);
|
||||
}
|
@ -1,303 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: Beerware
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctm.h"
|
||||
#define BADREAD 32
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Pass2 -- Validate the incoming CTM-file.
|
||||
*/
|
||||
|
||||
int
|
||||
Pass2(FILE *fd)
|
||||
{
|
||||
u_char *p,*q,*md5=0;
|
||||
MD5_CTX ctx;
|
||||
int i,j,sep,cnt,fdesc;
|
||||
u_char *trash=0,*name=0;
|
||||
struct CTM_Syntax *sp;
|
||||
struct stat st;
|
||||
int ret = 0;
|
||||
int match = 0;
|
||||
char md5_1[33];
|
||||
struct CTM_Filter *filter;
|
||||
FILE *ed = NULL;
|
||||
static char *template = NULL;
|
||||
|
||||
if(Verbose>3)
|
||||
printf("Pass2 -- Checking if CTM-patch will apply\n");
|
||||
MD5Init (&ctx);
|
||||
|
||||
GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
|
||||
/* XXX Lookup name in /etc/ctm,conf, read stuff */
|
||||
GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
|
||||
/* XXX Verify that this is the next patch to apply */
|
||||
GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
|
||||
GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
|
||||
/* XXX drop or use ? */
|
||||
|
||||
for(;;) {
|
||||
Delete(trash);
|
||||
Delete(name);
|
||||
Delete(md5);
|
||||
cnt = -1;
|
||||
|
||||
/* if a filter list was specified, check file name against
|
||||
the filters specified
|
||||
if no filter was given operate on all files. */
|
||||
match = (FilterList ?
|
||||
!(FilterList->Action) : CTM_FILTER_ENABLE);
|
||||
|
||||
GETFIELD(p,' ');
|
||||
|
||||
if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
|
||||
|
||||
if(!strcmp(p+3,"_END"))
|
||||
break;
|
||||
|
||||
for(sp=Syntax;sp->Key;sp++)
|
||||
if(!strcmp(p+3,sp->Key))
|
||||
goto found;
|
||||
WRONG
|
||||
found:
|
||||
for(i=0;(j = sp->List[i]);i++) {
|
||||
if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
|
||||
sep = ' ';
|
||||
else
|
||||
sep = '\n';
|
||||
|
||||
switch (j & CTM_F_MASK) {
|
||||
case CTM_F_Name:
|
||||
GETNAMECOPY(name,sep,j,0);
|
||||
/* If `keep' was specified, we won't remove any files,
|
||||
so don't check if the file exists */
|
||||
if (KeepIt &&
|
||||
(!strcmp(sp->Key,"FR") || !strcmp(sp->Key,"DR"))) {
|
||||
match = CTM_FILTER_DISABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
for (filter = FilterList; filter; filter = filter->Next) if (0 == regexec(&filter->CompiledRegex, name,
|
||||
0, 0, 0)) {
|
||||
match = filter->Action;
|
||||
}
|
||||
|
||||
if (CTM_FILTER_DISABLE == match)
|
||||
break; /* should ignore this file */
|
||||
|
||||
/* XXX Check DR DM rec's for parent-dir */
|
||||
if(j & CTM_Q_Name_New) {
|
||||
/* XXX Check DR FR rec's for item */
|
||||
if(-1 != stat(name,&st)) {
|
||||
fprintf(stderr," %s: %s exists.\n",
|
||||
sp->Key,name);
|
||||
ret |= Exit_Forcible;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(-1 == stat(name,&st)) {
|
||||
fprintf(stderr," %s: %s doesn't exist.\n",
|
||||
sp->Key,name);
|
||||
if (sp->Key[1] == 'R')
|
||||
ret |= Exit_Forcible;
|
||||
else
|
||||
ret |= Exit_NotOK;
|
||||
break;
|
||||
}
|
||||
if (SetTime && getuid() && (getuid() != st.st_uid)) {
|
||||
fprintf(stderr,
|
||||
" %s: %s not mine, cannot set time.\n",
|
||||
sp->Key,name);
|
||||
ret |= Exit_NotOK;
|
||||
}
|
||||
if (j & CTM_Q_Name_Dir) {
|
||||
if((st.st_mode & S_IFMT) != S_IFDIR) {
|
||||
fprintf(stderr,
|
||||
" %s: %s exist, but isn't dir.\n",
|
||||
sp->Key,name);
|
||||
ret |= Exit_NotOK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (j & CTM_Q_Name_File) {
|
||||
if((st.st_mode & S_IFMT) != S_IFREG) {
|
||||
fprintf(stderr,
|
||||
" %s: %s exist, but isn't file.\n",
|
||||
sp->Key,name);
|
||||
ret |= Exit_NotOK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CTM_F_Uid:
|
||||
case CTM_F_Gid:
|
||||
case CTM_F_Mode:
|
||||
GETFIELD(p,sep);
|
||||
break;
|
||||
case CTM_F_MD5:
|
||||
if(!name) WRONG
|
||||
if(j & CTM_Q_MD5_Before) {
|
||||
char *tmp;
|
||||
GETFIELD(p,sep);
|
||||
if(match && (st.st_mode & S_IFMT) == S_IFREG &&
|
||||
(tmp = MD5File(name,md5_1)) != NULL &&
|
||||
strcmp(tmp,p)) {
|
||||
fprintf(stderr," %s: %s md5 mismatch.\n",
|
||||
sp->Key,name);
|
||||
GETFIELDCOPY(md5,sep);
|
||||
if(md5 != NULL && strcmp(tmp,md5) == 0) {
|
||||
fprintf(stderr," %s: %s already applied.\n",
|
||||
sp->Key,name);
|
||||
match = CTM_FILTER_DISABLE;
|
||||
} else if(j & CTM_Q_MD5_Force) {
|
||||
if(Force)
|
||||
fprintf(stderr," Can and will force.\n");
|
||||
else
|
||||
fprintf(stderr," Could have forced.\n");
|
||||
ret |= Exit_Forcible;
|
||||
} else {
|
||||
ret |= Exit_NotOK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if(j & CTM_Q_MD5_After) {
|
||||
if(md5 == NULL) {
|
||||
GETFIELDCOPY(md5,sep);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Unqualified MD5 */
|
||||
WRONG
|
||||
break;
|
||||
case CTM_F_Count:
|
||||
GETBYTECNT(cnt,sep);
|
||||
break;
|
||||
case CTM_F_Bytes:
|
||||
if(cnt < 0) WRONG
|
||||
GETDATA(trash,cnt);
|
||||
if (!match)
|
||||
break;
|
||||
if (!template) {
|
||||
if (asprintf(&template, "%s/CTMclientXXXXXX",
|
||||
TmpDir) == -1) {
|
||||
fprintf(stderr, " %s: malloc failed.\n",
|
||||
sp->Key);
|
||||
ret |= Exit_Mess;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if(!strcmp(sp->Key,"FN")) {
|
||||
if ((p = strdup(template)) == NULL) {
|
||||
fprintf(stderr, " %s: malloc failed.\n",
|
||||
sp->Key);
|
||||
ret |= Exit_Mess;
|
||||
return ret;
|
||||
}
|
||||
if ((fdesc = mkstemp(p)) == -1) {
|
||||
fprintf(stderr, " %s: mkstemp failed.\n",
|
||||
sp->Key);
|
||||
ret |= Exit_Mess;
|
||||
Free(p);
|
||||
return ret;
|
||||
}
|
||||
if (close(fdesc) == -1) {
|
||||
fprintf(stderr, " %s: close failed.\n",
|
||||
sp->Key);
|
||||
ret |= Exit_Mess;
|
||||
unlink(p);
|
||||
Free(p);
|
||||
return ret;
|
||||
}
|
||||
j = ctm_edit(trash,cnt,name,p);
|
||||
if(j) {
|
||||
fprintf(stderr," %s: %s edit returned %d.\n",
|
||||
sp->Key,name,j);
|
||||
ret |= j;
|
||||
unlink(p);
|
||||
Free(p);
|
||||
return ret;
|
||||
} else if(strcmp(md5,MD5File(p,md5_1))) {
|
||||
fprintf(stderr," %s: %s edit fails.\n",
|
||||
sp->Key,name);
|
||||
ret |= Exit_Mess;
|
||||
unlink(p);
|
||||
Free(p);
|
||||
return ret;
|
||||
}
|
||||
unlink(p);
|
||||
Free(p);
|
||||
} else if (!strcmp(sp->Key,"FE")) {
|
||||
if ((p = strdup(template)) == NULL) {
|
||||
fprintf(stderr, " %s: malloc failed.\n",
|
||||
sp->Key);
|
||||
ret |= Exit_Mess;
|
||||
return ret;
|
||||
}
|
||||
if ((fdesc = mkstemp(p)) == -1) {
|
||||
fprintf(stderr, " %s: mkstemp failed.\n",
|
||||
sp->Key);
|
||||
ret |= Exit_Mess;
|
||||
Free(p);
|
||||
return ret;
|
||||
}
|
||||
if (close(fdesc) == -1) {
|
||||
fprintf(stderr, " %s: close failed.\n",
|
||||
sp->Key);
|
||||
ret |= Exit_Mess;
|
||||
unlink(p);
|
||||
Free(p);
|
||||
return ret;
|
||||
}
|
||||
ed = popen("ed","w");
|
||||
if (!ed) {
|
||||
WRONG
|
||||
}
|
||||
fprintf(ed,"e %s\n", name);
|
||||
if (cnt != fwrite(trash,1,cnt,ed)) {
|
||||
warn("%s", name);
|
||||
pclose(ed);
|
||||
WRONG
|
||||
}
|
||||
fprintf(ed,"w %s\n",p);
|
||||
if (pclose(ed)) {
|
||||
warn("%s", p);
|
||||
WRONG
|
||||
}
|
||||
if(strcmp(md5,MD5File(p,md5_1))) {
|
||||
fprintf(stderr,"%s %s MD5 didn't come out right\n",
|
||||
sp->Key, name);
|
||||
WRONG
|
||||
}
|
||||
unlink(p);
|
||||
Free(p);
|
||||
}
|
||||
|
||||
break;
|
||||
default: WRONG
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Delete(trash);
|
||||
Delete(name);
|
||||
Delete(md5);
|
||||
|
||||
q = MD5End (&ctx,md5_1);
|
||||
GETFIELD(p,'\n'); /* <MD5> */
|
||||
if(strcmp(q,p)) WRONG
|
||||
if (-1 != getc(fd)) WRONG
|
||||
return ret;
|
||||
}
|
@ -1,297 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: Beerware
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctm.h"
|
||||
#define BADREAD 32
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Pass3 -- Validate the incoming CTM-file.
|
||||
*/
|
||||
|
||||
int
|
||||
settime(const char *name, const struct timeval *times)
|
||||
{
|
||||
if (SetTime)
|
||||
if (utimes(name,times)) {
|
||||
warn("utimes(): %s", name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Pass3(FILE *fd)
|
||||
{
|
||||
u_char *p,*q,buf[BUFSIZ];
|
||||
MD5_CTX ctx;
|
||||
int i,j,sep,cnt;
|
||||
u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
|
||||
struct CTM_Syntax *sp;
|
||||
FILE *ed=0;
|
||||
struct stat st;
|
||||
char md5_1[33];
|
||||
int match=0;
|
||||
struct timeval times[2];
|
||||
struct CTM_Filter *filter = NULL;
|
||||
if(Verbose>3)
|
||||
printf("Pass3 -- Applying the CTM-patch\n");
|
||||
MD5Init (&ctx);
|
||||
|
||||
GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
|
||||
GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
|
||||
|
||||
/*
|
||||
* This would be cleaner if mktime() worked in UTC rather than
|
||||
* local time.
|
||||
*/
|
||||
if (SetTime) {
|
||||
struct tm tm;
|
||||
char *tz;
|
||||
char buf[5];
|
||||
int i;
|
||||
|
||||
#define SUBSTR(off,len) strncpy(buf, &TimeStamp[off], len), buf[len] = '\0'
|
||||
#define WRONGDATE { fprintf(stderr, " %s failed date validation\n",\
|
||||
TimeStamp); WRONG}
|
||||
|
||||
if (strlen(TimeStamp) != 15 || TimeStamp[14] != 'Z') WRONGDATE
|
||||
for (i = 0; i < 14; i++)
|
||||
if (!isdigit(TimeStamp[i])) WRONGDATE
|
||||
|
||||
tz = getenv("TZ");
|
||||
if (setenv("TZ", "UTC", 1) < 0) WRONG
|
||||
tzset();
|
||||
|
||||
tm.tm_isdst = tm.tm_gmtoff = 0;
|
||||
|
||||
SUBSTR(0, 4);
|
||||
tm.tm_year = atoi(buf) - 1900;
|
||||
SUBSTR(4, 2);
|
||||
tm.tm_mon = atoi(buf) - 1;
|
||||
if (tm.tm_mon < 0 || tm.tm_mon > 11) WRONGDATE
|
||||
SUBSTR(6, 2);
|
||||
tm.tm_mday = atoi(buf);
|
||||
if (tm.tm_mday < 1 || tm.tm_mday > 31) WRONG;
|
||||
SUBSTR(8, 2);
|
||||
tm.tm_hour = atoi(buf);
|
||||
if (tm.tm_hour > 24) WRONGDATE
|
||||
SUBSTR(10, 2);
|
||||
tm.tm_min = atoi(buf);
|
||||
if (tm.tm_min > 59) WRONGDATE
|
||||
SUBSTR(12, 2);
|
||||
tm.tm_sec = atoi(buf);
|
||||
if (tm.tm_min > 62) WRONGDATE /* allow leap seconds */
|
||||
|
||||
times[0].tv_sec = times[1].tv_sec = mktime(&tm);
|
||||
if (times[0].tv_sec == -1) WRONGDATE
|
||||
times[0].tv_usec = times[1].tv_usec = 0;
|
||||
|
||||
if (tz) {
|
||||
if (setenv("TZ", tz, 1) < 0) WRONGDATE
|
||||
} else {
|
||||
unsetenv("TZ");
|
||||
}
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
Delete(md5);
|
||||
Delete(uid);
|
||||
Delete(gid);
|
||||
Delete(mode);
|
||||
Delete(md5before);
|
||||
Delete(trash);
|
||||
Delete(name);
|
||||
cnt = -1;
|
||||
|
||||
GETFIELD(p,' ');
|
||||
|
||||
if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
|
||||
|
||||
if(!strcmp(p+3,"_END"))
|
||||
break;
|
||||
|
||||
for(sp=Syntax;sp->Key;sp++)
|
||||
if(!strcmp(p+3,sp->Key))
|
||||
goto found;
|
||||
WRONG
|
||||
found:
|
||||
for(i=0;(j = sp->List[i]);i++) {
|
||||
if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
|
||||
sep = ' ';
|
||||
else
|
||||
sep = '\n';
|
||||
|
||||
switch (j & CTM_F_MASK) {
|
||||
case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
|
||||
case CTM_F_Uid: GETFIELDCOPY(uid,sep); break;
|
||||
case CTM_F_Gid: GETFIELDCOPY(gid,sep); break;
|
||||
case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
|
||||
case CTM_F_MD5:
|
||||
if(j & CTM_Q_MD5_Before)
|
||||
GETFIELDCOPY(md5before,sep);
|
||||
else
|
||||
GETFIELDCOPY(md5,sep);
|
||||
break;
|
||||
case CTM_F_Count: GETBYTECNT(cnt,sep); break;
|
||||
case CTM_F_Bytes: GETDATA(trash,cnt); break;
|
||||
default: WRONG
|
||||
}
|
||||
}
|
||||
/* XXX This should go away. Disallow trailing '/' */
|
||||
j = strlen(name)-1;
|
||||
if(name[j] == '/') name[j] = '\0';
|
||||
|
||||
/*
|
||||
* If a filter list is specified, run thru the filter list and
|
||||
* match `name' against filters. If the name matches, set the
|
||||
* required action to that specified in the filter.
|
||||
* The default action if no filterlist is given is to match
|
||||
* everything.
|
||||
*/
|
||||
|
||||
match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
|
||||
for (filter = FilterList; filter; filter = filter->Next) {
|
||||
if (0 == regexec(&filter->CompiledRegex, name,
|
||||
0, 0, 0)) {
|
||||
match = filter->Action;
|
||||
}
|
||||
}
|
||||
|
||||
if (CTM_FILTER_DISABLE == match) /* skip file if disabled */
|
||||
continue;
|
||||
|
||||
if (Verbose > 0)
|
||||
fprintf(stderr,"> %s %s\n",sp->Key,name);
|
||||
if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) {
|
||||
i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666);
|
||||
if(i < 0) {
|
||||
warn("%s", name);
|
||||
WRONG
|
||||
}
|
||||
if(cnt != write(i,trash,cnt)) {
|
||||
warn("%s", name);
|
||||
WRONG
|
||||
}
|
||||
close(i);
|
||||
if(strcmp(md5,MD5File(name,md5_1))) {
|
||||
fprintf(stderr," %s %s MD5 didn't come out right\n",
|
||||
sp->Key,name);
|
||||
WRONG
|
||||
}
|
||||
if (settime(name,times)) WRONG
|
||||
continue;
|
||||
}
|
||||
if(!strcmp(sp->Key,"FE")) {
|
||||
ed = popen("ed","w");
|
||||
if(!ed) {
|
||||
WRONG
|
||||
}
|
||||
fprintf(ed,"e %s\n",name);
|
||||
if(cnt != fwrite(trash,1,cnt,ed)) {
|
||||
warn("%s", name);
|
||||
pclose(ed);
|
||||
WRONG
|
||||
}
|
||||
fprintf(ed,"w %s\n",name);
|
||||
if(pclose(ed)) {
|
||||
warn("ed");
|
||||
WRONG
|
||||
}
|
||||
if(strcmp(md5,MD5File(name,md5_1))) {
|
||||
fprintf(stderr," %s %s MD5 didn't come out right\n",
|
||||
sp->Key,name);
|
||||
WRONG
|
||||
}
|
||||
if (settime(name,times)) WRONG
|
||||
continue;
|
||||
}
|
||||
if(!strcmp(sp->Key,"FN")) {
|
||||
strcpy(buf,name);
|
||||
strcat(buf,TMPSUFF);
|
||||
i = ctm_edit(trash,cnt,name,buf);
|
||||
if(i) {
|
||||
fprintf(stderr," %s %s Edit failed with code %d.\n",
|
||||
sp->Key,name,i);
|
||||
WRONG
|
||||
}
|
||||
if(strcmp(md5,MD5File(buf,md5_1))) {
|
||||
fprintf(stderr," %s %s Edit failed MD5 check.\n",
|
||||
sp->Key,name);
|
||||
WRONG
|
||||
}
|
||||
if (rename(buf,name) == -1)
|
||||
WRONG
|
||||
if (settime(name,times)) WRONG
|
||||
continue;
|
||||
}
|
||||
if(!strcmp(sp->Key,"DM")) {
|
||||
if(0 > mkdir(name,0777)) {
|
||||
sprintf(buf,"mkdir -p %s",name);
|
||||
system(buf);
|
||||
}
|
||||
if(0 > stat(name,&st) || ((st.st_mode & S_IFMT) != S_IFDIR)) {
|
||||
fprintf(stderr,"<%s> mkdir failed\n",name);
|
||||
WRONG
|
||||
}
|
||||
if (settime(name,times)) WRONG
|
||||
continue;
|
||||
}
|
||||
if(!strcmp(sp->Key,"FR")) {
|
||||
if (KeepIt) {
|
||||
if (Verbose > 1)
|
||||
printf("<%s> not removed\n", name);
|
||||
}
|
||||
else if (0 != unlink(name)) {
|
||||
fprintf(stderr,"<%s> unlink failed\n",name);
|
||||
if (!Force)
|
||||
WRONG
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(!strcmp(sp->Key,"DR")) {
|
||||
/*
|
||||
* We cannot use rmdir() because we do not get the directories
|
||||
* in '-depth' order (cvs-cur.0018.gz for examples)
|
||||
*/
|
||||
if (KeepIt) {
|
||||
if (Verbose > 1) {
|
||||
printf("<%s> not removed\n", name);
|
||||
}
|
||||
} else {
|
||||
sprintf(buf,"rm -rf %s",name);
|
||||
system(buf);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
WRONG
|
||||
}
|
||||
|
||||
Delete(md5);
|
||||
Delete(uid);
|
||||
Delete(gid);
|
||||
Delete(mode);
|
||||
Delete(md5before);
|
||||
Delete(trash);
|
||||
Delete(name);
|
||||
|
||||
q = MD5End (&ctx,md5_1);
|
||||
GETFIELD(p,'\n');
|
||||
if(strcmp(q,p)) WRONG
|
||||
if (-1 != getc(fd)) WRONG
|
||||
return 0;
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: Beerware
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <koshy@india.hp.com> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joseph Koshy
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctm.h"
|
||||
#define BADREAD 32
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* PassB -- Backup modified files.
|
||||
*/
|
||||
|
||||
int
|
||||
PassB(FILE *fd)
|
||||
{
|
||||
u_char *p,*q;
|
||||
MD5_CTX ctx;
|
||||
int i,j,sep,cnt;
|
||||
u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
|
||||
struct CTM_Syntax *sp;
|
||||
FILE *b = 0; /* backup command */
|
||||
u_char buf[BUFSIZ];
|
||||
char md5_1[33];
|
||||
int ret = 0;
|
||||
int match = 0;
|
||||
struct CTM_Filter *filter = NULL;
|
||||
|
||||
if(Verbose>3)
|
||||
printf("PassB -- Backing up files which would be changed.\n");
|
||||
|
||||
MD5Init (&ctx);
|
||||
snprintf(buf, sizeof(buf), fmtcheck(TarCmd, TARCMD), BackupFile);
|
||||
b=popen(buf, "w");
|
||||
if(!b) { warn("%s", buf); return Exit_Garbage; }
|
||||
|
||||
GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
|
||||
GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
|
||||
GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
|
||||
|
||||
for(;;) {
|
||||
Delete(md5);
|
||||
Delete(uid);
|
||||
Delete(gid);
|
||||
Delete(mode);
|
||||
Delete(md5before);
|
||||
Delete(trash);
|
||||
Delete(name);
|
||||
cnt = -1;
|
||||
|
||||
GETFIELD(p,' ');
|
||||
|
||||
if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
|
||||
|
||||
if(!strcmp(p+3,"_END"))
|
||||
break;
|
||||
|
||||
for(sp=Syntax;sp->Key;sp++)
|
||||
if(!strcmp(p+3,sp->Key))
|
||||
goto found;
|
||||
WRONG
|
||||
found:
|
||||
for(i=0;(j = sp->List[i]);i++) {
|
||||
if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
|
||||
sep = ' ';
|
||||
else
|
||||
sep = '\n';
|
||||
|
||||
switch (j & CTM_F_MASK) {
|
||||
case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
|
||||
case CTM_F_Uid: GETFIELDCOPY(uid,sep); break;
|
||||
case CTM_F_Gid: GETFIELDCOPY(gid,sep); break;
|
||||
case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
|
||||
case CTM_F_MD5:
|
||||
if(j & CTM_Q_MD5_Before)
|
||||
GETFIELDCOPY(md5before,sep);
|
||||
else
|
||||
GETFIELDCOPY(md5,sep);
|
||||
break;
|
||||
case CTM_F_Count: GETBYTECNT(cnt,sep); break;
|
||||
case CTM_F_Bytes: GETDATA(trash,cnt); break;
|
||||
default: WRONG
|
||||
}
|
||||
}
|
||||
/* XXX This should go away. Disallow trailing '/' */
|
||||
j = strlen(name)-1;
|
||||
if(name[j] == '/') name[j] = '\0';
|
||||
|
||||
if (KeepIt &&
|
||||
(!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR")))
|
||||
continue;
|
||||
|
||||
/* match the name against the elements of the filter list. The
|
||||
action associated with the last matched filter determines whether
|
||||
this file should be ignored or backed up. */
|
||||
match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
|
||||
for (filter = FilterList; filter; filter = filter->Next) {
|
||||
if (0 == regexec(&filter->CompiledRegex, name, 0, 0, 0))
|
||||
match = filter->Action;
|
||||
}
|
||||
|
||||
if (CTM_FILTER_DISABLE == match)
|
||||
continue;
|
||||
|
||||
if (!strcmp(sp->Key,"FS") || !strcmp(sp->Key,"FN") ||
|
||||
!strcmp(sp->Key,"AS") || !strcmp(sp->Key,"DR") ||
|
||||
!strcmp(sp->Key,"FR")) {
|
||||
/* send name to the archiver for a backup */
|
||||
cnt = strlen(name);
|
||||
if (cnt != fwrite(name,1,cnt,b) || EOF == fputc('\n',b)) {
|
||||
warn("%s", name);
|
||||
pclose(b);
|
||||
WRONG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = pclose(b);
|
||||
|
||||
Delete(md5);
|
||||
Delete(uid);
|
||||
Delete(gid);
|
||||
Delete(mode);
|
||||
Delete(md5before);
|
||||
Delete(trash);
|
||||
Delete(name);
|
||||
|
||||
q = MD5End (&ctx,md5_1);
|
||||
GETFIELD(p,'\n'); /* <MD5> */
|
||||
if(strcmp(q,p)) WRONG
|
||||
if (-1 != getc(fd)) WRONG
|
||||
return ret;
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: Beerware
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctm.h"
|
||||
|
||||
/* The fields... */
|
||||
#define Name CTM_F_Name
|
||||
#define Uid CTM_F_Uid
|
||||
#define Gid CTM_F_Gid
|
||||
#define Mode CTM_F_Mode
|
||||
#define MD5 CTM_F_MD5
|
||||
#define Count CTM_F_Count
|
||||
#define Bytes CTM_F_Bytes
|
||||
|
||||
/* The qualifiers... */
|
||||
#define File CTM_Q_Name_File
|
||||
#define Dir CTM_Q_Name_Dir
|
||||
#define New CTM_Q_Name_New
|
||||
#define Subst CTM_Q_Name_Subst
|
||||
#define After CTM_Q_MD5_After
|
||||
#define Before CTM_Q_MD5_Before
|
||||
#define Chunk CTM_Q_MD5_Chunk
|
||||
#define Force CTM_Q_MD5_Force
|
||||
|
||||
static int ctmFM[] = /* File Make */
|
||||
{ Name|File|New|Subst, Uid, Gid, Mode,
|
||||
MD5|After|Chunk, Count, Bytes,0 };
|
||||
|
||||
static int ctmFS[] = /* File Substitute */
|
||||
{ Name|File|Subst, Uid, Gid, Mode,
|
||||
MD5|Before|Force, MD5|After|Chunk, Count, Bytes,0 };
|
||||
|
||||
static int ctmFE[] = /* File Edit */
|
||||
{ Name|File|Subst, Uid, Gid, Mode,
|
||||
MD5|Before, MD5|After, Count, Bytes,0 };
|
||||
|
||||
static int ctmFR[] = /* File Remove */
|
||||
{ Name|File|Subst, MD5|Before, 0 };
|
||||
|
||||
static int ctmAS[] = /* Attribute Substitute */
|
||||
{ Name|Subst, Uid, Gid, Mode, 0 };
|
||||
|
||||
static int ctmDM[] = /* Directory Make */
|
||||
{ Name|Dir|New , Uid, Gid, Mode, 0 };
|
||||
|
||||
static int ctmDR[] = /* Directory Remove */
|
||||
{ Name|Dir, 0 };
|
||||
|
||||
struct CTM_Syntax Syntax[] = {
|
||||
{ "FM", ctmFM },
|
||||
{ "FS", ctmFS },
|
||||
{ "FE", ctmFE },
|
||||
{ "FN", ctmFE },
|
||||
{ "FR", ctmFR },
|
||||
{ "AS", ctmAS },
|
||||
{ "DM", ctmDM },
|
||||
{ "DR", ctmDR },
|
||||
{ 0, 0} };
|
@ -1,13 +0,0 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR:H}/ctm_rmail
|
||||
|
||||
PROG= ctm_dequeue
|
||||
MAN=
|
||||
SRCS= ctm_dequeue.c error.c
|
||||
|
||||
CFLAGS+= -I${.CURDIR:H}/ctm_rmail
|
||||
|
||||
WARNS?= 1
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -1,17 +0,0 @@
|
||||
# $FreeBSD$
|
||||
# Autogenerated - do NOT edit!
|
||||
|
||||
DIRDEPS = \
|
||||
gnu/lib/csu \
|
||||
include \
|
||||
include/xlocale \
|
||||
lib/${CSU_DIR} \
|
||||
lib/libc \
|
||||
lib/libcompiler_rt \
|
||||
|
||||
|
||||
.include <dirdeps.mk>
|
||||
|
||||
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||
# local dependencies - needed for -jN in clean tree
|
||||
.endif
|
@ -1,212 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 1996, Gary J. Palmer
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer,
|
||||
* verbatim and that no modifications are made prior to this
|
||||
* point in the file.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* ctm_dequeue: Dequeue queued delta pieces and mail them.
|
||||
*
|
||||
* The pieces have already been packaged up as mail messages by ctm_smail,
|
||||
* and will be simply passed to sendmail in alphabetical order.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fts.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include "error.h"
|
||||
#include "options.h"
|
||||
|
||||
#define DEFAULT_NUM 1 /* Default number of pieces mailed per run. */
|
||||
|
||||
int fts_sort(const FTSENT * const *, const FTSENT * const *);
|
||||
int run_sendmail(int ifd);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *log_file = NULL;
|
||||
char *queue_dir = NULL;
|
||||
char *list[2];
|
||||
int num_to_send = DEFAULT_NUM, chunk;
|
||||
int fd;
|
||||
FTS *fts;
|
||||
FTSENT *ftsent;
|
||||
int piece, npieces;
|
||||
char filename[PATH_MAX];
|
||||
|
||||
err_prog_name(argv[0]);
|
||||
|
||||
OPTIONS("[-l log] [-n num] queuedir")
|
||||
NUMBER('n', num_to_send)
|
||||
STRING('l', log_file)
|
||||
ENDOPTS
|
||||
|
||||
if (argc != 2)
|
||||
usage();
|
||||
|
||||
if (log_file)
|
||||
err_set_log(log_file);
|
||||
|
||||
queue_dir = argv[1];
|
||||
list[0] = queue_dir;
|
||||
list[1] = NULL;
|
||||
|
||||
fts = fts_open(list, FTS_PHYSICAL|FTS_COMFOLLOW, fts_sort);
|
||||
if (fts == NULL)
|
||||
{
|
||||
err("fts failed on `%s'", queue_dir);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ftsent = fts_read(fts);
|
||||
if (ftsent == NULL || ftsent->fts_info != FTS_D)
|
||||
{
|
||||
err("not a directory: %s", queue_dir);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ftsent = fts_children(fts, 0);
|
||||
if (ftsent == NULL && errno)
|
||||
{
|
||||
err("*ftschildren failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (chunk = 1; ftsent != NULL; ftsent = ftsent->fts_link)
|
||||
{
|
||||
/*
|
||||
* Skip non-files and ctm_smail tmp files (ones starting with `.')
|
||||
*/
|
||||
if (ftsent->fts_info != FTS_F || ftsent->fts_name[0] == '.')
|
||||
continue;
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s", queue_dir,
|
||||
ftsent->fts_name);
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
err("*open: %s", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (run_sendmail(fd))
|
||||
exit(1);
|
||||
|
||||
close(fd);
|
||||
|
||||
if (unlink(filename) < 0)
|
||||
{
|
||||
err("*unlink: %s", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deduce the delta, piece number, and number of pieces from
|
||||
* the name of the file in the queue. Ideally, we should be
|
||||
* able to get the mail alias name too.
|
||||
*
|
||||
* NOTE: This depends intimately on the queue name used in ctm_smail.
|
||||
*/
|
||||
npieces = atoi(&ftsent->fts_name[ftsent->fts_namelen-3]);
|
||||
piece = atoi(&ftsent->fts_name[ftsent->fts_namelen-7]);
|
||||
err("%.*s %d/%d sent", (int)(ftsent->fts_namelen-8), ftsent->fts_name,
|
||||
piece, npieces);
|
||||
|
||||
if (chunk++ == num_to_send)
|
||||
break;
|
||||
}
|
||||
|
||||
fts_close(fts);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
int
|
||||
fts_sort(const FTSENT * const * a, const FTSENT * const * b)
|
||||
{
|
||||
if ((*a)->fts_info != FTS_F)
|
||||
return(0);
|
||||
if ((*a)->fts_info != FTS_F)
|
||||
return(0);
|
||||
|
||||
return (strcmp((*a)->fts_name, (*b)->fts_name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Run sendmail with the given file descriptor as standard input.
|
||||
* Sendmail will decode the destination from the message contents.
|
||||
* Returns 0 on success, 1 on failure.
|
||||
*/
|
||||
int
|
||||
run_sendmail(int ifd)
|
||||
{
|
||||
pid_t child, pid;
|
||||
int status;
|
||||
|
||||
switch (child = fork())
|
||||
{
|
||||
case -1:
|
||||
err("*fork");
|
||||
return(1);
|
||||
|
||||
case 0: /* Child */
|
||||
dup2(ifd, 0);
|
||||
execl(_PATH_SENDMAIL, _PATH_SENDMAIL, "-odq", "-t", (char *)NULL);
|
||||
err("*exec: %s", _PATH_SENDMAIL);
|
||||
_exit(1);
|
||||
|
||||
default: /* Parent */
|
||||
while ((pid = wait(&status)) != child)
|
||||
{
|
||||
if (pid == -1 && errno != EINTR)
|
||||
{
|
||||
err("*wait");
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
{
|
||||
err("sendmail failed");
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= ctm_rmail
|
||||
MLINKS= ctm_rmail.1 ctm_smail.1 \
|
||||
ctm_rmail.1 ctm_dequeue.1
|
||||
SRCS= ctm_rmail.c error.c
|
||||
|
||||
WARNS?= 2
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -1,17 +0,0 @@
|
||||
# $FreeBSD$
|
||||
# Autogenerated - do NOT edit!
|
||||
|
||||
DIRDEPS = \
|
||||
gnu/lib/csu \
|
||||
include \
|
||||
include/xlocale \
|
||||
lib/${CSU_DIR} \
|
||||
lib/libc \
|
||||
lib/libcompiler_rt \
|
||||
|
||||
|
||||
.include <dirdeps.mk>
|
||||
|
||||
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||
# local dependencies - needed for -jN in clean tree
|
||||
.endif
|
@ -1,510 +0,0 @@
|
||||
.\" NOTICE: This is free documentation. I hope you get some use from these
|
||||
.\" words. In return you should think about all the nice people who sweat
|
||||
.\" blood to document their free software. Maybe you should write some
|
||||
.\" documentation and give it away. Maybe with a free program attached!
|
||||
.\"
|
||||
.\" Author: Stephen McKay
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd January 24, 1995
|
||||
.Dt CTM_MAIL 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm ctm_smail ,
|
||||
.Nm ctm_dequeue ,
|
||||
.Nm ctm_rmail
|
||||
.Nd send and receive
|
||||
.Xr ctm 1
|
||||
deltas via mail
|
||||
.Sh SYNOPSIS
|
||||
.Nm ctm_smail
|
||||
.Op Fl l Ar log
|
||||
.Op Fl m Ar maxmsgsize
|
||||
.Op Fl c Ar maxctmsize
|
||||
.Op Fl q Ar queue-dir
|
||||
.Ar ctm-delta
|
||||
.Ar mail-alias
|
||||
.Nm ctm_dequeue
|
||||
.Op Fl l Ar log
|
||||
.Op Fl n Ar numchunks
|
||||
.Ar queue-dir
|
||||
.Nm ctm_rmail
|
||||
.Op Fl Dfuv
|
||||
.Op Fl l Ar log
|
||||
.Op Fl p Ar piecedir
|
||||
.Op Fl d Ar deltadir
|
||||
.Op Fl b Ar basedir
|
||||
.Op Ar
|
||||
.Sh DESCRIPTION
|
||||
In conjunction with the
|
||||
.Xr ctm 1
|
||||
command,
|
||||
.Nm ctm_smail ,
|
||||
.Nm ctm_dequeue
|
||||
and
|
||||
.Nm ctm_rmail
|
||||
are used to distribute changes to a source tree via email.
|
||||
The
|
||||
.Nm ctm_smail
|
||||
utility is given a compressed
|
||||
.Xr ctm
|
||||
delta, and a mailing list to send it to.
|
||||
It splits the delta into manageable
|
||||
pieces, encodes them as mail messages and sends them to the mailing list
|
||||
(optionally queued to spread the mail load).
|
||||
Each recipient uses
|
||||
.Nm ctm_rmail
|
||||
(either manually or automatically) to decode and reassemble the delta, and
|
||||
optionally call
|
||||
.Xr ctm
|
||||
to apply it to the source tree.
|
||||
At the moment,
|
||||
several source trees are distributed, and by several sites.
|
||||
These include
|
||||
the
|
||||
.Fx Ns -current
|
||||
source and CVS trees, distributed by
|
||||
.Li freefall.FreeBSD.org .
|
||||
.Pp
|
||||
Command line arguments for
|
||||
.Nm ctm_smail :
|
||||
.Bl -tag -width indent
|
||||
.It Fl l Ar log
|
||||
Instead of appearing on
|
||||
.Em stderr ,
|
||||
error diagnostics and informational messages (other than command line errors)
|
||||
are time stamped and written to the file
|
||||
.Em log .
|
||||
.It Fl m Ar maxmsgsize
|
||||
Limit the maximum size mail message that
|
||||
.Nm ctm_smail
|
||||
is allowed to send.
|
||||
It is approximate since mail headers and other niceties
|
||||
are not counted in this limit.
|
||||
If not specified, it will default to 64000
|
||||
bytes, leaving room for 1535 bytes of headers before the rumoured 64k mail
|
||||
limit.
|
||||
.It Fl c Ar maxctmsize
|
||||
Limit the maximum size delta that will be sent.
|
||||
Deltas bigger that this
|
||||
limit will cause an apology mail message to be sent to the mailing list.
|
||||
This is to prevent massive changes overwhelming users' mail boxes.
|
||||
Note that
|
||||
this is the size before encoding.
|
||||
Encoding causes a 4/3 size increase before
|
||||
mail headers are added.
|
||||
If not specified, there is no limit.
|
||||
.It Fl q Ar queue-dir
|
||||
Instead of mailing the delta pieces now, store them in the given directory
|
||||
to be mailed later using
|
||||
.Nm ctm_dequeue .
|
||||
This feature allows the mailing of large deltas to be spread out over
|
||||
hours or even days to limit the impact on recipients with limited network
|
||||
bandwidth or small mail spool areas.
|
||||
.El
|
||||
.Pp
|
||||
.Ar ctm-delta
|
||||
is the delta to be sent, and
|
||||
.Ar mail-alias
|
||||
is the mailing list to send the delta to.
|
||||
The mail messages are sent using
|
||||
.Xr sendmail 8 .
|
||||
.Pp
|
||||
Command line arguments for
|
||||
.Nm ctm_dequeue :
|
||||
.Bl -tag -width indent
|
||||
.It Fl l Ar log
|
||||
Instead of appearing on
|
||||
.Em stderr ,
|
||||
error diagnostics and informational messages (other than command line errors)
|
||||
are time stamped and written to the file
|
||||
.Em log .
|
||||
.It Fl n Ar numchunks
|
||||
Limit the number of mail messages that
|
||||
.Nm ctm_dequeue
|
||||
will send per run.
|
||||
By default,
|
||||
.Nm ctm_dequeue
|
||||
will send one mail message per run.
|
||||
.El
|
||||
.Pp
|
||||
.Ar queuedir
|
||||
is the directory containing the mail messages stored by
|
||||
.Nm ctm_smail .
|
||||
Up to
|
||||
.Ar numchunks
|
||||
mail messages will be sent in each run.
|
||||
The recipient mailing list is already
|
||||
encoded in the queued files.
|
||||
.Pp
|
||||
It is safe to run
|
||||
.Nm ctm_dequeue
|
||||
while
|
||||
.Nm ctm_smail
|
||||
is adding entries to the queue, or even to run
|
||||
.Nm ctm_smail
|
||||
multiple times concurrently, but a separate queue directory should be used
|
||||
for each tree being distributed.
|
||||
This is because entries are served in
|
||||
alphabetical order, and one tree will be unfairly serviced before any others,
|
||||
based on the delta names, not delta creation times.
|
||||
.Pp
|
||||
Command line arguments for
|
||||
.Nm ctm_rmail :
|
||||
.Bl -tag -width indent
|
||||
.It Fl l Ar log
|
||||
Instead of appearing on
|
||||
.Em stderr ,
|
||||
error diagnostics and informational messages (other than command line errors)
|
||||
are time stamped and written to the file
|
||||
.Em log .
|
||||
.It Fl p Ar piecedir
|
||||
Collect pieces of deltas in this directory.
|
||||
Each piece corresponds to a
|
||||
single mail message.
|
||||
Pieces are removed when complete deltas are built.
|
||||
If this flag is not given, no input files will be read, but completed
|
||||
deltas may still be applied with
|
||||
.Xr ctm
|
||||
if the
|
||||
.Fl b
|
||||
flag is given.
|
||||
.It Fl d Ar deltadir
|
||||
Collect completed deltas in this directory.
|
||||
Deltas are built from one or
|
||||
more pieces when all pieces are present.
|
||||
.It Fl b Ar basedir
|
||||
Apply any completed deltas to this source tree.
|
||||
If this flag is not given,
|
||||
deltas will be stored, but not applied.
|
||||
The user may then apply the deltas
|
||||
manually, or by using
|
||||
.Nm ctm_rmail
|
||||
without the
|
||||
.Fl p
|
||||
flag.
|
||||
Deltas will not be applied if they do not match the
|
||||
.Li .ctm_status
|
||||
file in
|
||||
.Ar basedir
|
||||
(or if
|
||||
.Li .ctm_status
|
||||
does not exist).
|
||||
.It Fl D
|
||||
Delete deltas after successful application by
|
||||
.Xr ctm .
|
||||
It is probably a good idea to avoid this flag (and keep all the deltas) as
|
||||
.Xr ctm
|
||||
has the ability to recover small groups of files from a full set of deltas.
|
||||
.It Fl f
|
||||
Fork and execute in the background while applying deltas with
|
||||
.Xr ctm .
|
||||
This is useful when automatically invoking
|
||||
.Nm ctm_rmail
|
||||
from
|
||||
.Xr sendmail
|
||||
because
|
||||
.Xr ctm
|
||||
can take a very long time to complete, causing other people's mail to
|
||||
be delayed, and can in theory cause spurious
|
||||
mail retransmission due to the remote
|
||||
.Xr sendmail
|
||||
timing out, or even termination of
|
||||
.Nm ctm_rmail
|
||||
by mail filters such as
|
||||
.Xr "MH's"
|
||||
.Xr slocal .
|
||||
Do not worry about zillions of background
|
||||
.Xr ctm
|
||||
processes loading your machine, since locking is used to prevent more than one
|
||||
.Xr ctm
|
||||
invocation at a time.
|
||||
.It Fl u
|
||||
Pass the
|
||||
.Fl u
|
||||
flag to the
|
||||
.Xr ctm
|
||||
command when applying the complete deltas, causing it to set the modification
|
||||
time of created and modified files to the CTM delta creation time.
|
||||
.It Fl v
|
||||
Pass the
|
||||
.Fl v
|
||||
flag to the
|
||||
.Xr ctm
|
||||
command when applying the complete deltas, causing a more informative
|
||||
output.
|
||||
All
|
||||
.Xr ctm
|
||||
output appears in the
|
||||
.Nm ctm_rmail
|
||||
log file.
|
||||
.El
|
||||
.Pp
|
||||
The file arguments (or
|
||||
.Em stdin ,
|
||||
if there are none) are scanned for delta pieces.
|
||||
Multiple delta pieces
|
||||
can be read from a single file, so an entire maildrop can be scanned
|
||||
and processed with a single command.
|
||||
.Pp
|
||||
It is safe to invoke
|
||||
.Nm ctm_rmail
|
||||
multiple times concurrently (with different input files),
|
||||
as might happen when
|
||||
.Xr sendmail
|
||||
is delivering mail asynchronously.
|
||||
This is because locking is used to
|
||||
keep things orderly.
|
||||
.Sh FILE FORMAT
|
||||
Following are the important parts of an actual (very small) delta piece:
|
||||
.Bd -literal
|
||||
From: owner-src-cur
|
||||
To: src-cur
|
||||
Subject: ctm-mail src-cur.0003.gz 1/4
|
||||
|
||||
CTM_MAIL BEGIN src-cur.0003.gz 1 4
|
||||
H4sIAAAAAAACA3VU72/bNhD9bP0VByQoEiyRSZEUSQP9kKTeYCR2gDTdsGFAwB/HRogtG5K8NCj6
|
||||
v4+UZSdtUQh6Rz0eee/xaF/dzx8up3/MFlDkBNrGnbttAwyo1pxoRgoiBNX/QJ5d3c9/X8DcPGGo
|
||||
lggkPiXngE4W1gUjKPJCYyk5MZRbIqmNW/ASglIFcdwIzTUxaAqhnCPcBqloKEkJVNDMF0Azk+Bo
|
||||
dDzzk0Ods/+A5gXv9YyJHjMCtJwQNeESNma7hOmXDRxn
|
||||
CTM_MAIL END 61065
|
||||
.Ed
|
||||
.Pp
|
||||
The subject of the message always begins with
|
||||
.Dq ctm-mail
|
||||
followed by the name of the delta, which piece this is, and how many total
|
||||
pieces there are.
|
||||
The data are bracketed by
|
||||
.Dq CTM_MAIL BEGIN
|
||||
and
|
||||
.Dq CTM_MAIL END
|
||||
lines, duplicating the information in the subject line, plus a simple checksum.
|
||||
.Pp
|
||||
If the delta exceeds
|
||||
.Ar maxctmsize ,
|
||||
then a message like this will be received instead:
|
||||
.Bd -literal
|
||||
From: owner-src-cur
|
||||
To: src-cur
|
||||
Subject: ctm-notice src-cur.0999.gz
|
||||
|
||||
src-cur.0999.gz is 792843 bytes. The limit is 300000 bytes.
|
||||
|
||||
You can retrieve this delta via ftp.
|
||||
.Ed
|
||||
.Pp
|
||||
You are then on your own!
|
||||
.Sh ENVIRONMENT
|
||||
If deltas are to be applied then
|
||||
.Xr ctm 1
|
||||
and
|
||||
.Xr gunzip 1
|
||||
must be in your
|
||||
.Ev PATH .
|
||||
.Sh FILES
|
||||
.Bl -tag -width indent
|
||||
.It Pa QUEUEDIR/*
|
||||
Pieces of deltas encoded as mail messages waiting to be sent to the
|
||||
mailing list.
|
||||
.It Pa PIECEDIR/*
|
||||
Pieces of deltas waiting for the rest to arrive.
|
||||
.It Pa DELTADIR/*
|
||||
Completed deltas.
|
||||
.It Pa BASEDIR/.ctm_status
|
||||
File containing the name and number of the next delta to be applied to this
|
||||
source tree.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
The
|
||||
.Nm ctm_smail ,
|
||||
.Nm ctm_dequeue
|
||||
and
|
||||
.Nm ctm_rmail
|
||||
utilities return exit status 0 for success, and 1 for various failures.
|
||||
The
|
||||
.Nm ctm_rmail
|
||||
utility is expected to be called from a mail transfer program, and thus signals
|
||||
failure only when the input mail message should be bounced (preferably into
|
||||
your regular maildrop, not back to the sender).
|
||||
In short, failure to
|
||||
apply a completed delta with
|
||||
.Xr ctm
|
||||
is not considered an error important enough to bounce the mail, and
|
||||
.Nm ctm_rmail
|
||||
returns an exit status of 0.
|
||||
.Sh EXAMPLES
|
||||
To send delta 32 of
|
||||
.Em src-cur
|
||||
to a group of wonderful code hackers known to
|
||||
.Xr sendmail
|
||||
as
|
||||
.Em src-guys ,
|
||||
limiting the mail size to roughly 60000 bytes, you could use:
|
||||
.Bd -literal -offset indent
|
||||
ctm_smail -m 60000 /wherever/it/is/src-cur.0032.gz src-guys
|
||||
.Ed
|
||||
.Pp
|
||||
To decode every
|
||||
.Nm ctm-mail
|
||||
message in your mailbox, assemble them into complete deltas, then apply
|
||||
any deltas built or lying around, you could use:
|
||||
.Bd -literal -offset indent
|
||||
ctm_rmail -p ~/pieces -d ~/deltas -b /usr/ctm-src-cur $MAIL
|
||||
.Ed
|
||||
.Pp
|
||||
(Note that no messages are deleted by
|
||||
.Nm ctm_rmail .
|
||||
Any mail reader could be used for that purpose.)
|
||||
.Pp
|
||||
To create a mail alias called
|
||||
.Em receiver-dude
|
||||
that will automatically decode and assemble deltas, but not apply them,
|
||||
you could put the following lines in your
|
||||
.Pa /etc/mail/aliases
|
||||
file (assuming the
|
||||
.Pa /ctm/tmp
|
||||
and
|
||||
.Pa /ctm/deltas
|
||||
directories and
|
||||
.Pa /ctm/log
|
||||
file are writable by user
|
||||
.Em daemon
|
||||
or group
|
||||
.Em wheel ) :
|
||||
.Bd -literal -offset indent
|
||||
receiver-dude: "|ctm_rmail -p /ctm/tmp -d /ctm/deltas -l /ctm/log"
|
||||
owner-receiver-dude: real_dude@wherever.you.like
|
||||
.Ed
|
||||
.Pp
|
||||
The second line will catch failures and drop them into your regular mailbox,
|
||||
or wherever else you like.
|
||||
.Pp
|
||||
To apply all the deltas collected, and delete those applied, you could use:
|
||||
.Bd -literal -offset indent
|
||||
ctm_rmail -D -d /ctm/deltas -b /ctm/src-cur -l /ctm/apply.log
|
||||
.Ed
|
||||
.Pp
|
||||
For maximum flexibility, consider this excerpt from a
|
||||
.Xr procmail
|
||||
script:
|
||||
.Bd -literal -offset indent
|
||||
PATH=$HOME/bin:$PATH
|
||||
|
||||
:0 w
|
||||
* ^Subject: ctm-mail cvs-cur
|
||||
| ctm_incoming
|
||||
.Ed
|
||||
.Pp
|
||||
together with the
|
||||
shell script
|
||||
.Pa ~/bin/ctm_incoming :
|
||||
.Bd -literal -offset indent
|
||||
#! /bin/sh
|
||||
PATH="$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"
|
||||
export PATH
|
||||
|
||||
cd $HOME/ctm && ctm_rmail -f -p pieces -d deltas -l log -b /ctm
|
||||
.Ed
|
||||
.Pp
|
||||
which will deposit all
|
||||
.Xr ctm
|
||||
deltas in
|
||||
.Pa ~/ctm/deltas ,
|
||||
apply them to the tree in
|
||||
.Pa /ctm ,
|
||||
and drop any failures into your regular mail box.
|
||||
Note the
|
||||
.Ev PATH
|
||||
manipulation in
|
||||
.Pa ctm_incoming
|
||||
which allows
|
||||
.Nm ctm_rmail
|
||||
to execute
|
||||
.Xr ctm 1
|
||||
on the
|
||||
.Pq non- Ns Fx
|
||||
machine that this example was taken from.
|
||||
.Sh SECURITY
|
||||
On its own, CTM is an insecure protocol
|
||||
- there is no authentication performed that the
|
||||
changes applied to the source code were sent by a
|
||||
trusted party, and so care should be taken if the
|
||||
CTM deltas are obtained via an unauthenticated
|
||||
medium such as regular email.
|
||||
It is a relatively simple matter for an attacker
|
||||
to forge a CTM delta to replace or precede the
|
||||
legitimate one and insert malicious code into your
|
||||
source tree.
|
||||
If the legitimate delta is somehow prevented from
|
||||
arriving, this will go unnoticed until a later
|
||||
delta attempts to touch the same file, at which
|
||||
point the MD5 checksum will fail.
|
||||
.Pp
|
||||
To remedy this insecurity, CTM delta pieces generated by
|
||||
FreeBSD.org are cryptographically signed in a
|
||||
format compatible with the GNU Privacy Guard
|
||||
utility, available in /usr/ports/security/gpg, and
|
||||
the Pretty Good Privacy v5 utility,
|
||||
/usr/ports/security/pgp5.
|
||||
The relevant public key can be obtained by
|
||||
fingering ctm@FreeBSD.org.
|
||||
.Pp
|
||||
CTM deltas which are thus signed cannot be
|
||||
undetectably altered by an attacker.
|
||||
Therefore it is recommended that you make use of
|
||||
GPG or PGP5 to verify the signatures if you
|
||||
receive your CTM deltas via email.
|
||||
.Sh DIAGNOSTICS
|
||||
In normal operation,
|
||||
.Nm ctm_smail
|
||||
will report messages like:
|
||||
.Bd -literal -offset indent
|
||||
ctm_smail: src-cur.0250.gz 1/2 sent to src-guys
|
||||
.Ed
|
||||
.Pp
|
||||
or, if queueing,
|
||||
.Bd -literal -offset indent
|
||||
ctm_smail: src-cur.0250.gz 1/2 queued for src-guys
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Nm ctm_dequeue
|
||||
utility will report messages like:
|
||||
.Bd -literal -offset indent
|
||||
ctm_dequeue: src-cur.0250.gz 1/2 sent
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Nm ctm_rmail
|
||||
utility will report messages like:
|
||||
.Bd -literal -offset indent
|
||||
ctm_rmail: src-cur.0250.gz 1/2 stored
|
||||
ctm_rmail: src-cur.0250.gz 2/2 stored
|
||||
ctm_rmail: src-cur.0250.gz complete
|
||||
.Ed
|
||||
.Pp
|
||||
If any of the input files do not contain a valid delta piece,
|
||||
.Nm ctm_rmail
|
||||
will report:
|
||||
.Bd -literal -offset indent
|
||||
ctm_rmail: message contains no delta
|
||||
.Ed
|
||||
.Pp
|
||||
and return an exit status of 1.
|
||||
You can use this to redirect wayward messages
|
||||
back into your real mailbox if your mail filter goes wonky.
|
||||
.Pp
|
||||
These messages go to
|
||||
.Em stderr
|
||||
or to the log file.
|
||||
Messages from
|
||||
.Xr ctm 1
|
||||
turn up here too.
|
||||
Error messages should be self explanatory.
|
||||
.Sh SEE ALSO
|
||||
.Xr ctm 1 ,
|
||||
.Xr ctm 5
|
||||
.\" .Sh HISTORY
|
||||
.Sh AUTHORS
|
||||
.An Stephen McKay Aq Mt mckay@FreeBSD.org
|
@ -1,672 +0,0 @@
|
||||
/*
|
||||
* Accept one (or more) ASCII encoded chunks that together make a compressed
|
||||
* CTM delta. Decode them and reconstruct the deltas. Any completed
|
||||
* deltas may be passed to ctm for unpacking.
|
||||
*
|
||||
* Author: Stephen McKay
|
||||
*
|
||||
* NOTICE: This is free software. I hope you get some use from this program.
|
||||
* In return you should think about all the nice people who give away software.
|
||||
* Maybe you should write some free software too.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include "error.h"
|
||||
#include "options.h"
|
||||
|
||||
#define CTM_STATUS ".ctm_status"
|
||||
|
||||
char *piece_dir = NULL; /* Where to store pieces of deltas. */
|
||||
char *delta_dir = NULL; /* Where to store completed deltas. */
|
||||
char *base_dir = NULL; /* The tree to apply deltas to. */
|
||||
int delete_after = 0; /* Delete deltas after ctm applies them. */
|
||||
int apply_verbose = 0; /* Run with '-v' */
|
||||
int set_time = 0; /* Set the time of the files that is changed. */
|
||||
int mask = 0; /* The current umask */
|
||||
|
||||
void apply_complete(void);
|
||||
int read_piece(char *input_file);
|
||||
int combine_if_complete(char *delta, int pce, int npieces);
|
||||
int combine(char *delta, int npieces, char *dname, char *pname, char *tname);
|
||||
int decode_line(char *line, char *out_buf);
|
||||
int lock_file(char *name);
|
||||
|
||||
/*
|
||||
* If given a '-p' flag, read encoded delta pieces from stdin or file
|
||||
* arguments, decode them and assemble any completed deltas. If given
|
||||
* a '-b' flag, pass any completed deltas to 'ctm' for application to
|
||||
* the source tree. The '-d' flag is mandatory, but either of '-p' or
|
||||
* '-b' can be omitted. If given the '-l' flag, notes and errors will
|
||||
* be timestamped and written to the given file.
|
||||
*
|
||||
* Exit status is 0 for success or 1 for indigestible input. That is,
|
||||
* 0 means the encode input pieces were decoded and stored, and 1 means
|
||||
* some input was discarded. If a delta fails to apply, this won't be
|
||||
* reflected in the exit status. In this case, the delta is left in
|
||||
* 'deltadir'.
|
||||
*/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *log_file = NULL;
|
||||
int status = 0;
|
||||
int fork_ctm = 0;
|
||||
|
||||
mask = umask(0);
|
||||
umask(mask);
|
||||
|
||||
err_prog_name(argv[0]);
|
||||
|
||||
OPTIONS("[-Dfuv] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
|
||||
FLAG('D', delete_after)
|
||||
FLAG('f', fork_ctm)
|
||||
FLAG('u', set_time)
|
||||
FLAG('v', apply_verbose)
|
||||
STRING('p', piece_dir)
|
||||
STRING('d', delta_dir)
|
||||
STRING('b', base_dir)
|
||||
STRING('l', log_file)
|
||||
ENDOPTS
|
||||
|
||||
if (delta_dir == NULL)
|
||||
usage();
|
||||
|
||||
if (piece_dir == NULL && (base_dir == NULL || argc > 1))
|
||||
usage();
|
||||
|
||||
if (log_file != NULL)
|
||||
err_set_log(log_file);
|
||||
|
||||
/*
|
||||
* Digest each file in turn, or just stdin if no files were given.
|
||||
*/
|
||||
if (argc <= 1)
|
||||
{
|
||||
if (piece_dir != NULL)
|
||||
status = read_piece(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (*++argv != NULL)
|
||||
status |= read_piece(*argv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Maybe it's time to look for and apply completed deltas with ctm.
|
||||
*
|
||||
* Shall we report back to sendmail immediately, and let a child do
|
||||
* the work? Sendmail will be waiting for us to complete, delaying
|
||||
* other mail, and possibly some intermediate process (like MH slocal)
|
||||
* will terminate us if we take too long!
|
||||
*
|
||||
* If fork() fails, it's unlikely we'll be able to run ctm, so give up.
|
||||
* Also, the child exit status is unimportant.
|
||||
*/
|
||||
if (base_dir != NULL)
|
||||
if (!fork_ctm || fork() == 0)
|
||||
apply_complete();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct the file name of a piece of a delta.
|
||||
*/
|
||||
#define mk_piece_name(fn,d,p,n) \
|
||||
sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
|
||||
|
||||
/*
|
||||
* Construct the file name of an assembled delta.
|
||||
*/
|
||||
#define mk_delta_name(fn,d) \
|
||||
sprintf((fn), "%s/%s", delta_dir, (d))
|
||||
|
||||
/*
|
||||
* If the next required delta is now present, let ctm lunch on it and any
|
||||
* contiguous deltas.
|
||||
*/
|
||||
void
|
||||
apply_complete()
|
||||
{
|
||||
int i, dn;
|
||||
int lfd;
|
||||
FILE *fp, *ctm;
|
||||
struct stat sb;
|
||||
char class[20];
|
||||
char delta[30];
|
||||
char junk[2];
|
||||
char fname[PATH_MAX];
|
||||
char here[PATH_MAX];
|
||||
char buf[PATH_MAX*2];
|
||||
|
||||
/*
|
||||
* Grab a lock on the ctm mutex file so that we can be sure we are
|
||||
* working alone, not fighting another ctm_rmail!
|
||||
*/
|
||||
strcpy(fname, delta_dir);
|
||||
strcat(fname, "/.mutex_apply");
|
||||
if ((lfd = lock_file(fname)) < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Find out which delta ctm needs next.
|
||||
*/
|
||||
sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
|
||||
if ((fp = fopen(fname, "r")) == NULL)
|
||||
{
|
||||
close(lfd);
|
||||
return;
|
||||
}
|
||||
|
||||
i = fscanf(fp, "%19s %d %c", class, &dn, junk);
|
||||
fclose(fp);
|
||||
if (i != 2)
|
||||
{
|
||||
close(lfd);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We might need to convert the delta filename to an absolute pathname.
|
||||
*/
|
||||
here[0] = '\0';
|
||||
if (delta_dir[0] != '/')
|
||||
{
|
||||
getcwd(here, sizeof(here)-1);
|
||||
i = strlen(here) - 1;
|
||||
if (i >= 0 && here[i] != '/')
|
||||
{
|
||||
here[++i] = '/';
|
||||
here[++i] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep applying deltas until we run out or something bad happens.
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
sprintf(delta, "%s.%04d.gz", class, ++dn);
|
||||
mk_delta_name(fname, delta);
|
||||
|
||||
if (stat(fname, &sb) < 0)
|
||||
break;
|
||||
|
||||
sprintf(buf, "(cd %s && ctm %s%s%s%s) 2>&1", base_dir,
|
||||
set_time ? "-u " : "",
|
||||
apply_verbose ? "-v " : "", here, fname);
|
||||
if ((ctm = popen(buf, "r")) == NULL)
|
||||
{
|
||||
err("ctm failed to apply %s", delta);
|
||||
break;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), ctm) != NULL)
|
||||
{
|
||||
i = strlen(buf) - 1;
|
||||
if (i >= 0 && buf[i] == '\n')
|
||||
buf[i] = '\0';
|
||||
err("ctm: %s", buf);
|
||||
}
|
||||
|
||||
if (pclose(ctm) != 0)
|
||||
{
|
||||
err("ctm failed to apply %s", delta);
|
||||
break;
|
||||
}
|
||||
|
||||
if (delete_after)
|
||||
unlink(fname);
|
||||
|
||||
err("%s applied%s", delta, delete_after ? " and deleted" : "");
|
||||
}
|
||||
|
||||
/*
|
||||
* Closing the lock file clears the lock.
|
||||
*/
|
||||
close(lfd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This cheap plastic checksum effectively rotates our checksum-so-far
|
||||
* left one, then adds the character. We only want 16 bits of it, and
|
||||
* don't care what happens to the rest. It ain't much, but it's small.
|
||||
*/
|
||||
#define add_ck(sum,x) \
|
||||
((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
|
||||
|
||||
|
||||
/*
|
||||
* Decode the data between BEGIN and END, and stash it in the staging area.
|
||||
* Multiple pieces can be present in a single file, bracketed by BEGIN/END.
|
||||
* If we have all pieces of a delta, combine them. Returns 0 on success,
|
||||
* and 1 for any sort of failure.
|
||||
*/
|
||||
int
|
||||
read_piece(char *input_file)
|
||||
{
|
||||
int status = 0;
|
||||
FILE *ifp, *ofp = 0;
|
||||
int decoding = 0;
|
||||
int got_one = 0;
|
||||
int line_no = 0;
|
||||
int i, n;
|
||||
int pce, npieces;
|
||||
unsigned claimed_cksum;
|
||||
unsigned short cksum = 0;
|
||||
char out_buf[200];
|
||||
char line[200];
|
||||
char delta[30];
|
||||
char pname[PATH_MAX];
|
||||
char tname[PATH_MAX];
|
||||
char junk[2];
|
||||
|
||||
ifp = stdin;
|
||||
if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
|
||||
{
|
||||
err("cannot open '%s' for reading", input_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), ifp) != NULL)
|
||||
{
|
||||
line_no++;
|
||||
|
||||
/*
|
||||
* Remove all trailing white space.
|
||||
*/
|
||||
i = strlen(line) - 1;
|
||||
while (i > 0 && isspace(line[i]))
|
||||
line[i--] = '\0';
|
||||
|
||||
/*
|
||||
* Look for the beginning of an encoded piece.
|
||||
*/
|
||||
if (!decoding)
|
||||
{
|
||||
char *s;
|
||||
int fd = -1;
|
||||
|
||||
if (sscanf(line, "CTM_MAIL BEGIN %29s %d %d %c",
|
||||
delta, &pce, &npieces, junk) != 3)
|
||||
continue;
|
||||
|
||||
while ((s = strchr(delta, '/')) != NULL)
|
||||
*s = '_';
|
||||
|
||||
got_one++;
|
||||
strcpy(tname, piece_dir);
|
||||
strcat(tname, "/p.XXXXXXXXXX");
|
||||
if ((fd = mkstemp(tname)) == -1 ||
|
||||
(ofp = fdopen(fd, "w")) == NULL)
|
||||
{
|
||||
if (fd != -1) {
|
||||
err("cannot open '%s' for writing", tname);
|
||||
close(fd);
|
||||
}
|
||||
else
|
||||
err("*mkstemp: '%s'", tname);
|
||||
status++;
|
||||
continue;
|
||||
}
|
||||
|
||||
cksum = 0xffff;
|
||||
decoding++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are decoding. Stop if we see the end flag.
|
||||
*/
|
||||
if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
|
||||
{
|
||||
int e;
|
||||
|
||||
decoding = 0;
|
||||
|
||||
fflush(ofp);
|
||||
e = ferror(ofp);
|
||||
fclose(ofp);
|
||||
|
||||
if (e)
|
||||
err("error writing %s", tname);
|
||||
|
||||
if (cksum != claimed_cksum)
|
||||
err("checksum: read %d, calculated %d", claimed_cksum, cksum);
|
||||
|
||||
if (e || cksum != claimed_cksum)
|
||||
{
|
||||
err("%s %d/%d discarded", delta, pce, npieces);
|
||||
unlink(tname);
|
||||
status++;
|
||||
continue;
|
||||
}
|
||||
|
||||
mk_piece_name(pname, delta, pce, npieces);
|
||||
if (rename(tname, pname) < 0)
|
||||
{
|
||||
err("*rename: '%s' to '%s'", tname, pname);
|
||||
err("%s %d/%d lost!", delta, pce, npieces);
|
||||
unlink(tname);
|
||||
status++;
|
||||
continue;
|
||||
}
|
||||
|
||||
err("%s %d/%d stored", delta, pce, npieces);
|
||||
|
||||
if (!combine_if_complete(delta, pce, npieces))
|
||||
status++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be a line of encoded data. Decode it, sum it, and save it.
|
||||
*/
|
||||
n = decode_line(line, out_buf);
|
||||
if (n <= 0)
|
||||
{
|
||||
err("line %d: illegal character: '%c'", line_no, line[-n]);
|
||||
err("%s %d/%d discarded", delta, pce, npieces);
|
||||
|
||||
fclose(ofp);
|
||||
unlink(tname);
|
||||
|
||||
status++;
|
||||
decoding = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
add_ck(cksum, out_buf[i]);
|
||||
|
||||
fwrite(out_buf, sizeof(char), n, ofp);
|
||||
}
|
||||
|
||||
if (decoding)
|
||||
{
|
||||
err("truncated file");
|
||||
err("%s %d/%d discarded", delta, pce, npieces);
|
||||
|
||||
fclose(ofp);
|
||||
unlink(tname);
|
||||
|
||||
status++;
|
||||
}
|
||||
|
||||
if (ferror(ifp))
|
||||
{
|
||||
err("error reading %s", input_file == NULL ? "stdin" : input_file);
|
||||
status++;
|
||||
}
|
||||
|
||||
if (input_file != NULL)
|
||||
fclose(ifp);
|
||||
|
||||
if (!got_one)
|
||||
{
|
||||
err("message contains no delta");
|
||||
status++;
|
||||
}
|
||||
|
||||
return (status != 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Put the pieces together to form a delta, if they are all present.
|
||||
* Returns 1 on success (even if we didn't do anything), and 0 on failure.
|
||||
*/
|
||||
int
|
||||
combine_if_complete(char *delta, int pce, int npieces)
|
||||
{
|
||||
int i, e;
|
||||
int lfd;
|
||||
struct stat sb;
|
||||
char pname[PATH_MAX];
|
||||
char dname[PATH_MAX];
|
||||
char tname[PATH_MAX];
|
||||
|
||||
/*
|
||||
* We can probably just rename() it into place if it is a small delta.
|
||||
*/
|
||||
if (npieces == 1)
|
||||
{
|
||||
mk_delta_name(dname, delta);
|
||||
mk_piece_name(pname, delta, 1, 1);
|
||||
if (rename(pname, dname) == 0)
|
||||
{
|
||||
chmod(dname, 0666 & ~mask);
|
||||
err("%s complete", delta);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Grab a lock on the reassembly mutex file so that we can be sure we are
|
||||
* working alone, not fighting another ctm_rmail!
|
||||
*/
|
||||
strcpy(tname, delta_dir);
|
||||
strcat(tname, "/.mutex_build");
|
||||
if ((lfd = lock_file(tname)) < 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Are all of the pieces present? Of course the current one is,
|
||||
* unless all pieces are missing because another ctm_rmail has
|
||||
* processed them already.
|
||||
*/
|
||||
for (i = 1; i <= npieces; i++)
|
||||
{
|
||||
if (i == pce)
|
||||
continue;
|
||||
mk_piece_name(pname, delta, i, npieces);
|
||||
if (stat(pname, &sb) < 0)
|
||||
{
|
||||
close(lfd);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Stick them together. Let combine() use our file name buffers, since
|
||||
* we're such good buddies. :-)
|
||||
*/
|
||||
e = combine(delta, npieces, dname, pname, tname);
|
||||
close(lfd);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Put the pieces together to form a delta.
|
||||
* Returns 1 on success, and 0 on failure.
|
||||
* Note: dname, pname, and tname are room for some file names that just
|
||||
* happened to by lying around in the calling routine. Waste not, want not!
|
||||
*/
|
||||
int
|
||||
combine(char *delta, int npieces, char *dname, char *pname, char *tname)
|
||||
{
|
||||
FILE *dfp, *pfp;
|
||||
int i, n, e;
|
||||
char buf[BUFSIZ];
|
||||
int fd = -1;
|
||||
|
||||
strcpy(tname, delta_dir);
|
||||
strcat(tname, "/d.XXXXXXXXXX");
|
||||
if ((fd = mkstemp(tname)) == -1 ||
|
||||
(dfp = fdopen(fd, "w")) == NULL)
|
||||
{
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
err("cannot open '%s' for writing", tname);
|
||||
}
|
||||
else
|
||||
err("*mkstemp: '%s'", tname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reconstruct the delta by reading each piece in order.
|
||||
*/
|
||||
for (i = 1; i <= npieces; i++)
|
||||
{
|
||||
mk_piece_name(pname, delta, i, npieces);
|
||||
if ((pfp = fopen(pname, "r")) == NULL)
|
||||
{
|
||||
err("cannot open '%s' for reading", pname);
|
||||
fclose(dfp);
|
||||
unlink(tname);
|
||||
return 0;
|
||||
}
|
||||
while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0)
|
||||
fwrite(buf, sizeof(char), n, dfp);
|
||||
e = ferror(pfp);
|
||||
fclose(pfp);
|
||||
if (e)
|
||||
{
|
||||
err("error reading '%s'", pname);
|
||||
fclose(dfp);
|
||||
unlink(tname);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
fflush(dfp);
|
||||
e = ferror(dfp);
|
||||
fclose(dfp);
|
||||
if (e)
|
||||
{
|
||||
err("error writing '%s'", tname);
|
||||
unlink(tname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mk_delta_name(dname, delta);
|
||||
if (rename(tname, dname) < 0)
|
||||
{
|
||||
err("*rename: '%s' to '%s'", tname, dname);
|
||||
unlink(tname);
|
||||
return 0;
|
||||
}
|
||||
chmod(dname, 0666 & ~mask);
|
||||
|
||||
/*
|
||||
* Throw the pieces away.
|
||||
*/
|
||||
for (i = 1; i <= npieces; i++)
|
||||
{
|
||||
mk_piece_name(pname, delta, i, npieces);
|
||||
if (unlink(pname) < 0)
|
||||
err("*unlink: '%s'", pname);
|
||||
}
|
||||
|
||||
err("%s complete", delta);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MIME BASE64 decode table.
|
||||
*/
|
||||
static unsigned char from_b64[0x80] =
|
||||
{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
|
||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
|
||||
0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
||||
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
||||
0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Decode a line of ASCII into binary. Returns the number of bytes in
|
||||
* the output buffer, or < 0 on indigestable input. Error output is
|
||||
* the negative of the index of the inedible character.
|
||||
*/
|
||||
int
|
||||
decode_line(char *line, char *out_buf)
|
||||
{
|
||||
unsigned char *ip = (unsigned char *)line;
|
||||
unsigned char *op = (unsigned char *)out_buf;
|
||||
unsigned long bits;
|
||||
unsigned x;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
|
||||
break;
|
||||
bits = x << 18;
|
||||
ip++;
|
||||
if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
|
||||
{
|
||||
bits |= x << 12;
|
||||
*op++ = bits >> 16;
|
||||
ip++;
|
||||
if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
|
||||
{
|
||||
bits |= x << 6;
|
||||
*op++ = bits >> 8;
|
||||
ip++;
|
||||
if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
|
||||
{
|
||||
bits |= x;
|
||||
*op++ = bits;
|
||||
ip++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*ip == '\0' || *ip == '\n')
|
||||
return op - (unsigned char *)out_buf;
|
||||
else
|
||||
return -(ip - (unsigned char *)line);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create and lock the given file.
|
||||
*
|
||||
* Clearing the lock is as simple as closing the file descriptor we return.
|
||||
*/
|
||||
int
|
||||
lock_file(char *name)
|
||||
{
|
||||
int lfd;
|
||||
|
||||
if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0)
|
||||
{
|
||||
err("*open: '%s'", name);
|
||||
return -1;
|
||||
}
|
||||
if (flock(lfd, LOCK_EX) < 0)
|
||||
{
|
||||
close(lfd);
|
||||
err("*flock: '%s'", name);
|
||||
return -1;
|
||||
}
|
||||
return lfd;
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Routines for logging error messages or other informative messages.
|
||||
*
|
||||
* Log messages can easily contain the program name, a time stamp, system
|
||||
* error messages, and arbitrary printf-style strings, and can be directed
|
||||
* to stderr or a log file.
|
||||
*
|
||||
* Author: Stephen McKay
|
||||
*
|
||||
* NOTICE: This is free software. I hope you get some use from this program.
|
||||
* In return you should think about all the nice people who give away software.
|
||||
* Maybe you should write some free software too.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static const char rcsid[] =
|
||||
"$FreeBSD$";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include "error.h"
|
||||
|
||||
static FILE *error_fp = NULL;
|
||||
static char *prog = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Log errors to the given file.
|
||||
*/
|
||||
void
|
||||
err_set_log(char *log_file)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if ((fp = fopen(log_file, "a")) == NULL)
|
||||
err("cannot log to '%s'", log_file);
|
||||
else
|
||||
error_fp = fp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the error prefix if not logging to a file.
|
||||
*/
|
||||
void
|
||||
err_prog_name(char *name)
|
||||
{
|
||||
if ((prog = strrchr(name, '/')) == NULL)
|
||||
prog = name;
|
||||
else
|
||||
prog++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Log an error.
|
||||
*
|
||||
* A leading '*' in the message format means we want the system errno
|
||||
* decoded and appended.
|
||||
*/
|
||||
void
|
||||
err(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
time_t now;
|
||||
struct tm *tm;
|
||||
FILE *fp;
|
||||
int x = errno;
|
||||
int want_errno;
|
||||
|
||||
if ((fp = error_fp) == NULL)
|
||||
{
|
||||
fp = stderr;
|
||||
if (prog != NULL)
|
||||
fprintf(fp, "%s: ", prog);
|
||||
}
|
||||
else
|
||||
{
|
||||
time(&now);
|
||||
tm = localtime(&now);
|
||||
fprintf(fp, "%04d-%02d-%02d %02d:%02d ", tm->tm_year+1900,
|
||||
tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
|
||||
}
|
||||
|
||||
want_errno = 0;
|
||||
if (*fmt == '*')
|
||||
want_errno++, fmt++;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(fp, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (want_errno)
|
||||
fprintf(fp, ": %s", strerror(x));
|
||||
|
||||
fprintf(fp, "\n");
|
||||
fflush(fp);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
/* $FreeBSD$ */
|
||||
|
||||
extern void err_set_log(char *log_file);
|
||||
extern void err_prog_name(char *name);
|
||||
extern void err(const char *fmt, ...) __printflike(1, 2);
|
@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Macros for processing command arguments.
|
||||
*
|
||||
* Conforms closely to the command option requirements of intro(1) in System V
|
||||
* and intro(C) in Xenix.
|
||||
*
|
||||
* A command consists of: cmdname [ options ] [ cmdarguments ]
|
||||
*
|
||||
* Options consist of a leading dash '-' and a flag letter. An argument may
|
||||
* follow optionally preceded by white space.
|
||||
* Options without arguments may be grouped behind a single dash.
|
||||
* A dash on its own is interpreted as the end of the options and is retained
|
||||
* as a command argument.
|
||||
* A double dash '--' is interpreted as the end of the options and is discarded.
|
||||
*
|
||||
* For example:
|
||||
* zap -xz -f flame -q34 -- -x
|
||||
*
|
||||
* where zap.c contains the following in main():
|
||||
*
|
||||
* OPTIONS("[-xz] [-q queue-id] [-f dump-file] user")
|
||||
* FLAG('x', xecute)
|
||||
* FLAG('z', zot)
|
||||
* STRING('f', file)
|
||||
* fp = fopen(file, "w");
|
||||
* NUMBER('q', queue)
|
||||
* ENDOPTS
|
||||
*
|
||||
* Results in:
|
||||
* xecute = 1
|
||||
* zot = 1
|
||||
* file = "flame"
|
||||
* fp = fopen("flame", "w")
|
||||
* queue = 34
|
||||
* argc = 2
|
||||
* argv[0] = "zap"
|
||||
* argv[1] = "-x"
|
||||
*
|
||||
* Should the user enter unknown flags or leave out required arguments,
|
||||
* the message:
|
||||
*
|
||||
* Usage: zap [-xz] [-q queue-id] [-f dump-file] user
|
||||
*
|
||||
* will be printed. This message can be printed by calling pusage(), or
|
||||
* usage(). usage() will also cause program termination with exit code 1.
|
||||
*
|
||||
* Author: Stephen McKay, February 1991
|
||||
*
|
||||
* Based on recollection of the original options.h produced at the University
|
||||
* of Queensland by Ross Patterson (and possibly others).
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
static char *O_usage;
|
||||
static char *O_name;
|
||||
extern long atol();
|
||||
|
||||
void
|
||||
pusage()
|
||||
{
|
||||
/*
|
||||
* Avoid gratuitously loading stdio.
|
||||
*/
|
||||
write(STDERR_FILENO, "usage: ", 7);
|
||||
write(STDERR_FILENO, O_name, strlen(O_name));
|
||||
write(STDERR_FILENO, " ", 1);
|
||||
write(STDERR_FILENO, O_usage, strlen(O_usage));
|
||||
write(STDERR_FILENO, "\n", 1);
|
||||
}
|
||||
|
||||
#define usage() (pusage(), exit(1))
|
||||
|
||||
#define OPTIONS(usage_msg) \
|
||||
{ \
|
||||
char O_cont; \
|
||||
O_usage = (usage_msg); \
|
||||
O_name = argv[0]; \
|
||||
while (*++argv && **argv == '-') \
|
||||
{ \
|
||||
if ((*argv)[1] == '\0') \
|
||||
break; \
|
||||
argc--; \
|
||||
if ((*argv)[1] == '-' && (*argv)[2] == '\0') \
|
||||
{ \
|
||||
argv++; \
|
||||
break; \
|
||||
} \
|
||||
O_cont = 1; \
|
||||
while (O_cont) \
|
||||
switch (*++*argv) \
|
||||
{ \
|
||||
default: \
|
||||
case '-': \
|
||||
usage(); \
|
||||
case '\0': \
|
||||
O_cont = 0;
|
||||
|
||||
#define FLAG(x,flag) \
|
||||
break; \
|
||||
case (x): \
|
||||
(flag) = 1;
|
||||
|
||||
#define CHAR(x,ch) \
|
||||
break; \
|
||||
case (x): \
|
||||
O_cont = 0; \
|
||||
if (*++*argv == '\0' && (--argc, *++argv == 0)) \
|
||||
usage(); \
|
||||
(ch) = **argv;
|
||||
|
||||
#define NUMBER(x,n) \
|
||||
break; \
|
||||
case (x): \
|
||||
O_cont = 0; \
|
||||
if (*++*argv == '\0' && (--argc, *++argv == 0)) \
|
||||
usage(); \
|
||||
(n) = atol(*argv);
|
||||
|
||||
#define STRING(x,str) \
|
||||
break; \
|
||||
case (x): \
|
||||
O_cont = 0; \
|
||||
if (*++*argv == '\0' && (--argc, *++argv == 0)) \
|
||||
usage(); \
|
||||
(str) = *argv;
|
||||
|
||||
#define SUFFIX(x,str) \
|
||||
break; \
|
||||
case (x): \
|
||||
(str) = ++*argv; \
|
||||
O_cont = 0;
|
||||
|
||||
#define ENDOPTS \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
*--argv = O_name; \
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR:H}/ctm_rmail
|
||||
|
||||
PROG= ctm_smail
|
||||
MAN=
|
||||
SRCS= ctm_smail.c error.c
|
||||
|
||||
CFLAGS+= -I${.CURDIR:H}/ctm_rmail
|
||||
|
||||
WARNS?= 2
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -1,17 +0,0 @@
|
||||
# $FreeBSD$
|
||||
# Autogenerated - do NOT edit!
|
||||
|
||||
DIRDEPS = \
|
||||
gnu/lib/csu \
|
||||
include \
|
||||
include/xlocale \
|
||||
lib/${CSU_DIR} \
|
||||
lib/libc \
|
||||
lib/libcompiler_rt \
|
||||
|
||||
|
||||
.include <dirdeps.mk>
|
||||
|
||||
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||
# local dependencies - needed for -jN in clean tree
|
||||
.endif
|
@ -1,497 +0,0 @@
|
||||
/*
|
||||
* Send a compressed CTM delta to a recipient mailing list by encoding it
|
||||
* in safe ASCII characters, in mailer-friendly chunks, and passing them
|
||||
* to sendmail. Optionally, the chunks can be queued to be sent later by
|
||||
* ctm_dequeue in controlled bursts. The encoding is almost the same as
|
||||
* MIME BASE64, and is protected by a simple checksum.
|
||||
*
|
||||
* Author: Stephen McKay
|
||||
*
|
||||
* NOTICE: This is free software. I hope you get some use from this program.
|
||||
* In return you should think about all the nice people who give away software.
|
||||
* Maybe you should write some free software too.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include <limits.h>
|
||||
#include "error.h"
|
||||
#include "options.h"
|
||||
|
||||
#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */
|
||||
|
||||
#define LINE_LENGTH 72 /* Chars per encoded line. Divisible by 4. */
|
||||
|
||||
int chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
|
||||
long max_msg_size, char *mail_alias, char *queue_dir);
|
||||
int chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
|
||||
char *mail_alias);
|
||||
int chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
|
||||
char *mail_alias, char *queue_dir);
|
||||
void clean_up_queue(char *queue_dir);
|
||||
int encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum);
|
||||
void write_header(FILE *sfp, char *mail_alias, char *delta, int pce,
|
||||
int npieces);
|
||||
void write_trailer(FILE *sfp, unsigned sum);
|
||||
int apologise(char *delta, off_t ctm_size, long max_ctm_size,
|
||||
char *mail_alias, char *queue_dir);
|
||||
FILE *open_sendmail(void);
|
||||
int close_sendmail(FILE *fp);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int status = 0;
|
||||
char *delta_file;
|
||||
char *mail_alias;
|
||||
long max_msg_size = DEF_MAX_MSG;
|
||||
long max_ctm_size = 0;
|
||||
char *log_file = NULL;
|
||||
char *queue_dir = NULL;
|
||||
char *delta;
|
||||
FILE *dfp;
|
||||
struct stat sb;
|
||||
|
||||
err_prog_name(argv[0]);
|
||||
|
||||
OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] [-q queuedir] ctm-delta mail-alias")
|
||||
NUMBER('m', max_msg_size)
|
||||
NUMBER('c', max_ctm_size)
|
||||
STRING('l', log_file)
|
||||
STRING('q', queue_dir)
|
||||
ENDOPTS
|
||||
|
||||
if (argc != 3)
|
||||
usage();
|
||||
|
||||
if (log_file != NULL)
|
||||
err_set_log(log_file);
|
||||
|
||||
delta_file = argv[1];
|
||||
mail_alias = argv[2];
|
||||
|
||||
if ((delta = strrchr(delta_file, '/')) == NULL)
|
||||
delta = delta_file;
|
||||
else
|
||||
delta++;
|
||||
|
||||
if ((dfp = fopen(delta_file, "r")) == NULL || fstat(fileno(dfp), &sb) < 0)
|
||||
{
|
||||
err("*%s", delta_file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (max_ctm_size != 0 && sb.st_size > max_ctm_size)
|
||||
status = apologise(delta, sb.st_size, max_ctm_size, mail_alias,
|
||||
queue_dir);
|
||||
else
|
||||
status = chop_and_send_or_queue(dfp, delta, sb.st_size, max_msg_size,
|
||||
mail_alias, queue_dir);
|
||||
|
||||
fclose(dfp);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Carve our CTM delta into pieces, encode them, and send or queue them.
|
||||
* Returns 0 on success, and 1 on failure.
|
||||
*/
|
||||
int
|
||||
chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
|
||||
long max_msg_size, char *mail_alias, char *queue_dir)
|
||||
{
|
||||
int npieces;
|
||||
long msg_size;
|
||||
long exp_size;
|
||||
int status;
|
||||
|
||||
#undef howmany
|
||||
#define howmany(x,y) (((x)+((y)-1)) / (y))
|
||||
|
||||
/*
|
||||
* Work out how many pieces we need, bearing in mind that each piece
|
||||
* grows by 4/3 when encoded. We count the newlines too, but ignore
|
||||
* all mail headers and piece headers. They are a "small" (almost
|
||||
* constant) per message overhead that we make the user worry about. :-)
|
||||
*/
|
||||
exp_size = ctm_size * 4 / 3;
|
||||
exp_size += howmany(exp_size, LINE_LENGTH);
|
||||
npieces = howmany(exp_size, max_msg_size);
|
||||
msg_size = howmany(ctm_size, npieces);
|
||||
|
||||
#undef howmany
|
||||
|
||||
if (queue_dir == NULL)
|
||||
status = chop_and_send(dfp, delta, msg_size, npieces, mail_alias);
|
||||
else
|
||||
{
|
||||
status = chop_and_queue(dfp, delta, msg_size, npieces, mail_alias,
|
||||
queue_dir);
|
||||
if (status)
|
||||
clean_up_queue(queue_dir);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Carve our CTM delta into pieces, encode them, and send them.
|
||||
* Returns 0 on success, and 1 on failure.
|
||||
*/
|
||||
int
|
||||
chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
|
||||
char *mail_alias)
|
||||
{
|
||||
int pce;
|
||||
FILE *sfp;
|
||||
unsigned sum;
|
||||
|
||||
/*
|
||||
* Send each chunk directly to sendmail as it is generated.
|
||||
* No temporary files necessary. If things turn ugly, we just
|
||||
* have to live with the fact the we have sent only part of
|
||||
* the delta.
|
||||
*/
|
||||
for (pce = 1; pce <= npieces; pce++)
|
||||
{
|
||||
int read_error;
|
||||
|
||||
if ((sfp = open_sendmail()) == NULL)
|
||||
return 1;
|
||||
|
||||
write_header(sfp, mail_alias, delta, pce, npieces);
|
||||
read_error = encode_body(sfp, dfp, msg_size, &sum);
|
||||
if (!read_error)
|
||||
write_trailer(sfp, sum);
|
||||
|
||||
if (!close_sendmail(sfp) || read_error)
|
||||
return 1;
|
||||
|
||||
err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct the tmp queue file name of a delta piece.
|
||||
*/
|
||||
#define mk_tmp_name(fn,qd,p) \
|
||||
snprintf((fn), sizeof(fn), "%s/.%08ld.%03d", (qd), (long)getpid(), (p))
|
||||
|
||||
/*
|
||||
* Construct the final queue file name of a delta piece.
|
||||
*/
|
||||
#define mk_queue_name(fn,qd,d,p,n) \
|
||||
snprintf((fn), sizeof(fn), "%s/%s+%03d-%03d", (qd), (d), (p), (n))
|
||||
|
||||
/*
|
||||
* Carve our CTM delta into pieces, encode them, and queue them.
|
||||
* Returns 0 on success, and 1 on failure.
|
||||
*/
|
||||
int
|
||||
chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
|
||||
char *mail_alias, char *queue_dir)
|
||||
{
|
||||
int pce;
|
||||
FILE *qfp;
|
||||
unsigned sum;
|
||||
char tname[PATH_MAX];
|
||||
char qname[PATH_MAX];
|
||||
|
||||
/*
|
||||
* Store each piece in the queue directory, but under temporary names,
|
||||
* so that they can be deleted without unpleasant consequences if
|
||||
* anything goes wrong. We could easily fill up a disk, for example.
|
||||
*/
|
||||
for (pce = 1; pce <= npieces; pce++)
|
||||
{
|
||||
int write_error;
|
||||
|
||||
mk_tmp_name(tname, queue_dir, pce);
|
||||
if ((qfp = fopen(tname, "w")) == NULL)
|
||||
{
|
||||
err("cannot open '%s' for writing", tname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
write_header(qfp, mail_alias, delta, pce, npieces);
|
||||
if (encode_body(qfp, dfp, msg_size, &sum))
|
||||
return 1;
|
||||
write_trailer(qfp, sum);
|
||||
|
||||
fflush(qfp);
|
||||
write_error = ferror(qfp);
|
||||
fclose(qfp);
|
||||
if (write_error)
|
||||
{
|
||||
err("error writing '%s'", tname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Give the warm success message now, instead of all in a rush
|
||||
* during the rename phase.
|
||||
*/
|
||||
err("%s %d/%d queued for %s", delta, pce, npieces, mail_alias);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rename the pieces into place. If an error occurs now, we are
|
||||
* stuffed, but there is no neat way to back out. rename() should
|
||||
* only fail now under extreme circumstances.
|
||||
*/
|
||||
for (pce = 1; pce <= npieces; pce++)
|
||||
{
|
||||
mk_tmp_name(tname, queue_dir, pce);
|
||||
mk_queue_name(qname, queue_dir, delta, pce, npieces);
|
||||
if (rename(tname, qname) < 0)
|
||||
{
|
||||
err("*rename: '%s' to '%s'", tname, qname);
|
||||
unlink(tname);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* There may be temporary files cluttering up the queue directory.
|
||||
*/
|
||||
void
|
||||
clean_up_queue(char *queue_dir)
|
||||
{
|
||||
int pce;
|
||||
char tname[PATH_MAX];
|
||||
|
||||
err("discarding queued delta pieces");
|
||||
for (pce = 1; ; pce++)
|
||||
{
|
||||
mk_tmp_name(tname, queue_dir, pce);
|
||||
if (unlink(tname) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MIME BASE64 encode table.
|
||||
*/
|
||||
static char to_b64[0x40] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/*
|
||||
* This cheap plastic checksum effectively rotates our checksum-so-far
|
||||
* left one, then adds the character. We only want 16 bits of it, and
|
||||
* don't care what happens to the rest. It ain't much, but it's small.
|
||||
*/
|
||||
#define add_ck(sum,x) \
|
||||
((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
|
||||
|
||||
/*
|
||||
* Encode the body. Use an encoding almost the same as MIME BASE64.
|
||||
*
|
||||
* Characters are read from delta_fp and encoded characters are written
|
||||
* to sm_fp. At most 'msg_size' characters should be read from delta_fp.
|
||||
*
|
||||
* The body consists of lines of up to LINE_LENGTH characters. Each group
|
||||
* of 4 characters encodes 3 input characters. Each output character encodes
|
||||
* 6 bits. Thus 64 different characters are needed in this representation.
|
||||
*/
|
||||
int
|
||||
encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum)
|
||||
{
|
||||
unsigned short cksum = 0xffff;
|
||||
unsigned char *ip;
|
||||
char *op;
|
||||
int want, n, i;
|
||||
unsigned char inbuf[LINE_LENGTH*3/4];
|
||||
char outbuf[LINE_LENGTH+1];
|
||||
|
||||
/*
|
||||
* Round up to the nearest line boundary, for the tiniest of gains,
|
||||
* and lots of neatness. :-)
|
||||
*/
|
||||
msg_size += (LINE_LENGTH*3/4) - 1;
|
||||
msg_size -= msg_size % (LINE_LENGTH*3/4);
|
||||
|
||||
while (msg_size > 0)
|
||||
{
|
||||
want = (msg_size < sizeof(inbuf)) ? msg_size : sizeof(inbuf);
|
||||
if ((n = fread(inbuf, sizeof(char), want, delta_fp)) == 0)
|
||||
break;
|
||||
msg_size -= n;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
add_ck(cksum, inbuf[i]);
|
||||
|
||||
/*
|
||||
* Produce a line of encoded data. Every line length will be a
|
||||
* multiple of 4, except for, perhaps, the last line.
|
||||
*/
|
||||
ip = inbuf;
|
||||
op = outbuf;
|
||||
while (n >= 3)
|
||||
{
|
||||
*op++ = to_b64[ip[0] >> 2];
|
||||
*op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
|
||||
*op++ = to_b64[(ip[1] << 2 & 0x3f) | ip[2] >> 6];
|
||||
*op++ = to_b64[ip[2] & 0x3f];
|
||||
ip += 3;
|
||||
n -= 3;
|
||||
}
|
||||
if (n > 0)
|
||||
{
|
||||
*op++ = to_b64[ip[0] >> 2];
|
||||
*op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
|
||||
if (n >= 2)
|
||||
*op++ = to_b64[ip[1] << 2 & 0x3f];
|
||||
}
|
||||
*op++ = '\n';
|
||||
fwrite(outbuf, sizeof(char), op - outbuf, sm_fp);
|
||||
}
|
||||
|
||||
if (ferror(delta_fp))
|
||||
{
|
||||
err("error reading input file.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
*sum = cksum;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write the mail header and data header.
|
||||
*/
|
||||
void
|
||||
write_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces)
|
||||
{
|
||||
fprintf(sfp, "From: owner-%s\n", mail_alias);
|
||||
fprintf(sfp, "To: %s\n", mail_alias);
|
||||
fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", delta, pce, npieces);
|
||||
|
||||
fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", delta, pce, npieces);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write the data trailer.
|
||||
*/
|
||||
void
|
||||
write_trailer(FILE *sfp, unsigned sum)
|
||||
{
|
||||
fprintf(sfp, "CTM_MAIL END %ld\n", (long)sum);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We're terribly sorry, but the delta is too big to send.
|
||||
* Returns 0 on success, 1 on failure.
|
||||
*/
|
||||
int
|
||||
apologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias,
|
||||
char *queue_dir)
|
||||
{
|
||||
FILE *sfp;
|
||||
char qname[PATH_MAX];
|
||||
|
||||
if (queue_dir == NULL)
|
||||
{
|
||||
sfp = open_sendmail();
|
||||
if (sfp == NULL)
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mk_queue_name(qname, queue_dir, delta, 1, 1);
|
||||
sfp = fopen(qname, "w");
|
||||
if (sfp == NULL)
|
||||
{
|
||||
err("cannot open '%s' for writing", qname);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fprintf(sfp, "From: owner-%s\n", mail_alias);
|
||||
fprintf(sfp, "To: %s\n", mail_alias);
|
||||
fprintf(sfp, "Subject: ctm-notice %s\n\n", delta);
|
||||
|
||||
fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", delta,
|
||||
(long)ctm_size, max_ctm_size);
|
||||
fprintf(sfp, "You can retrieve this delta via ftp.\n");
|
||||
|
||||
if (queue_dir == NULL)
|
||||
{
|
||||
if (!close_sendmail(sfp))
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fclose(sfp)!=0)
|
||||
{
|
||||
err("error writing '%s'", qname);
|
||||
unlink(qname);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Start a pipe to sendmail. Sendmail will decode the destination
|
||||
* from the message contents.
|
||||
*/
|
||||
FILE *
|
||||
open_sendmail()
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[100];
|
||||
|
||||
sprintf(buf, "%s -odq -t", _PATH_SENDMAIL);
|
||||
if ((fp = popen(buf, "w")) == NULL)
|
||||
err("cannot start sendmail");
|
||||
return fp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Close a pipe to sendmail. Sendmail will then do its bit.
|
||||
* Return 1 on success, 0 on failure.
|
||||
*/
|
||||
int
|
||||
close_sendmail(FILE *fp)
|
||||
{
|
||||
int status;
|
||||
|
||||
fflush(fp);
|
||||
if (ferror(fp))
|
||||
{
|
||||
err("error writing to sendmail");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((status = pclose(fp)) != 0)
|
||||
err("sendmail failed with status %d", status);
|
||||
|
||||
return (status == 0);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= mkctm
|
||||
MAN=
|
||||
|
||||
LIBADD= md
|
||||
|
||||
test: mkctm
|
||||
rm -f tst.out*
|
||||
time ./mkctm -v -v /3c/210src /a/r1/usr/src \
|
||||
2>a | md5 -p > /a/tst.out
|
||||
ls -l /a/tst.out
|
||||
gzip -9 -v /a/tst.out
|
||||
ls -l /a/tst.out.gz
|
||||
# cd /usr/src/release && ctm -c -v -v ${.CURDIR}/tst.out
|
||||
|
||||
test1: mkctm
|
||||
rm -f tst.out*
|
||||
time ./mkctm -v -v /3c/210src /home/ncvs/src \
|
||||
2> b | md5 -p > /a/tst2.out
|
||||
ls -l /a/tst2.out
|
||||
gzip -9 -v /a/tst2.out
|
||||
ls -l /a/tst2.out.gz
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -1,9 +0,0 @@
|
||||
#!/usr/local/bin/tclsh
|
||||
|
||||
set CTMname cvs-cur
|
||||
set CTMref /home/ncvs
|
||||
set CTMignore {^src/secure|^src/eBones|^src/kerberosIV|^CVSROOT/val-tags$|CVSROOT/\\.#}
|
||||
set CTMbogus {\\.core$|/#cvs|/\\.#}
|
||||
set CTMmail ctm-cvs-cur-fast@freebsd.org
|
||||
set CTMqueuemail ctm-cvs-cur@freebsd.org
|
||||
set CTMqueue /home/ctm/queue/ctm-cvs-cur
|
@ -1,7 +0,0 @@
|
||||
#!/usr/local/bin/tclsh
|
||||
|
||||
set CTMname ports-cur
|
||||
set CTMref /usr/ports
|
||||
set CTMignore {/CVS$|/CVS/|^distfiles}
|
||||
set CTMbogus {\\.core$|/#cvs|/\\.#}
|
||||
set CTMmail ctm-ports-cur@freebsd.org
|
@ -1,7 +0,0 @@
|
||||
#!/usr/local/bin/tclsh
|
||||
|
||||
set CTMname smp-cur
|
||||
set CTMref /home/smp
|
||||
set CTMignore {^CVSROOT/history.*$|^CVSROOT/val-tags$|^CVSROOT/\\.#}
|
||||
set CTMbogus {\\.core$|/#cvs|/\\.#}
|
||||
set CTMmail smp-cvs-cur@freebsd.org
|
@ -1,9 +0,0 @@
|
||||
#!/usr/local/bin/tclsh
|
||||
|
||||
set CTMname src-cur
|
||||
set CTMref /c/src
|
||||
set CTMignore {/CVS$|/CVS/|^/secure|^/eBones}
|
||||
set CTMbogus {\\.core$|/#cvs|/\\.#}
|
||||
set CTMmail ctm-src-cur-fast@freebsd.org
|
||||
set CTMqueue /home/ctm/queue/ctm-src-cur
|
||||
set CTMqueuemail ctm-src-cur@freebsd.org
|
@ -1,9 +0,0 @@
|
||||
#!/usr/local/bin/tclsh
|
||||
|
||||
set CTMname src-cur
|
||||
set CTMref $CTMSW/../$CTMname
|
||||
set CTMcopy /c/phk/20R/usr/src
|
||||
set CTMdont {\.core$|/CVS$|/CVS/|^/secure|^/eBones|/#cvs|/\.#}
|
||||
set CTMtest 1
|
||||
set CTMspecial 1
|
||||
set CTMsuff R20
|
@ -1,6 +0,0 @@
|
||||
#! /bin/sh
|
||||
# $FreeBSD$
|
||||
|
||||
L=/home/ctm/log.dequeue
|
||||
/usr/sbin/ctm_dequeue -n 1 -l $L /home/ctm/queue/ctm-cvs-cur
|
||||
/usr/sbin/ctm_dequeue -n 1 -l $L /home/ctm/queue/ctm-src-cur
|
@ -1,188 +0,0 @@
|
||||
#!/usr/local/bin/tclsh7.4
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
#############################################################################
|
||||
### Do we already have this delta ?
|
||||
#############################################################################
|
||||
|
||||
proc find_delta {nbr} {
|
||||
global CTMname CTMdest
|
||||
if {[file exists [format "%s/$CTMname.%04d" $CTMdest $nbr]]} { return 1 }
|
||||
if {[file exists [format "%s/$CTMname.%04d.gz" $CTMdest $nbr]]} { return 1 }
|
||||
return 0
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
### The top level code...
|
||||
#############################################################################
|
||||
|
||||
set CTMSW /home/ctm/SW
|
||||
|
||||
cd $CTMSW
|
||||
|
||||
# Defaults...
|
||||
set CTMapply 1
|
||||
set CTMignore {^///}
|
||||
set CTMbogus {\.core$}
|
||||
set CTMmail {}
|
||||
set CTMqueue {}
|
||||
set CTMqueuemail {}
|
||||
set CTMmaxctm 10000000
|
||||
set CTMmaxmsg 100000
|
||||
set CTMsuff {}
|
||||
set CTMdate [exec date -u +%Y%m%d%H%M%SZ]
|
||||
set CTMtmp {}
|
||||
set CTMcopy {}
|
||||
set CTMdest {}
|
||||
set CTMprefix .
|
||||
set CTMtest 0
|
||||
set CTMspecial 0
|
||||
set CTMscan .
|
||||
set CTMfirst 0
|
||||
set max_damage 100
|
||||
|
||||
set damage 0
|
||||
set changes 0
|
||||
|
||||
source $argv
|
||||
exec sh -c "date -u '+%Y%m%d%H%M%S $argv'" >> ${CTMSW}/log
|
||||
|
||||
if {$CTMtmp == ""} {
|
||||
set CTMtmp $CTMSW/../tmp/${CTMname}_${CTMsuff}
|
||||
}
|
||||
if {$CTMcopy == ""} {
|
||||
set CTMcopy $CTMSW/../$CTMname
|
||||
}
|
||||
if {$CTMdest == ""} {
|
||||
set CTMdest $CTMSW/../CTM-pub/$CTMname
|
||||
}
|
||||
|
||||
# Make sure we only run one at a time...
|
||||
|
||||
set CTMlock Lck.${CTMname}.${CTMdate}.[pid]
|
||||
exec rm -f ${CTMlock}
|
||||
exec echo starting > ${CTMlock}
|
||||
if {[catch "exec ln $CTMlock LCK.$CTMname" a]} {
|
||||
puts "Not going, lock exists..."
|
||||
exec rm -f $CTMlock
|
||||
exit 1
|
||||
}
|
||||
exec rm -f $CTMlock
|
||||
set CTMlock LCK.$CTMname
|
||||
|
||||
set CTMscratch ${CTMtmp}.tmp
|
||||
|
||||
while 1 {
|
||||
if { ! $CTMspecial} {
|
||||
if {$CTMfirst} {
|
||||
set CTMnbr 0
|
||||
} else {
|
||||
set CTMnbr [lindex [exec cat $CTMcopy/.ctm_status] 1]
|
||||
}
|
||||
|
||||
if {$CTMnbr > 0 && ![find_delta $CTMnbr]} {
|
||||
puts "$CTMname delta $CTMnbr doesn't exist..."
|
||||
exec rm -f $CTMlock
|
||||
exit 1
|
||||
}
|
||||
|
||||
incr CTMnbr
|
||||
|
||||
if {[find_delta $CTMnbr]} {
|
||||
puts "$CTMname delta $CTMnbr does already exist..."
|
||||
exec rm -f $CTMlock
|
||||
exit 1
|
||||
}
|
||||
|
||||
set fo [open $CTMref/.ctm_status w]
|
||||
puts $fo "$CTMname $CTMnbr"
|
||||
close $fo
|
||||
incr changes -1
|
||||
|
||||
} else {
|
||||
set CTMnbr [lindex [exec cat $CTMref/.ctm_status] 1]
|
||||
}
|
||||
|
||||
puts "Doing CTMname $CTMname CTMnbr $CTMnbr$CTMsuff CTMdate $CTMdate"
|
||||
flush stdout
|
||||
exec sh -c "rm -f ${CTMtmp}.* ${CTMtmp}:*" >&@ stdout
|
||||
|
||||
set nm [format "%s.%04d%s" $CTMname $CTMnbr $CTMsuff]
|
||||
|
||||
set x1 $CTMcopy
|
||||
if {$x1 == ""} {
|
||||
exec mkdir ${CTMtmp}.dir
|
||||
set x1 ${CTMtmp}.dir
|
||||
}
|
||||
set r1 [catch "exec ${CTMSW}/mkctm -I ${CTMignore} -B ${CTMbogus} -l ${CTMtmp}.log -D $max_damage $CTMname $CTMnbr $CTMdate . $x1 $CTMref | md5 -p | gzip -9 > ${CTMtmp}:${nm}.gz 2>@ stderr" r2]
|
||||
|
||||
if {$r1} {
|
||||
if {[lindex $errorCode 2] == 4} {
|
||||
puts "No changes, stopping."
|
||||
exec rm -f $CTMlock
|
||||
exit 0
|
||||
}
|
||||
puts "problems, stopping now."
|
||||
puts "errorCode $errorCode"
|
||||
puts "$r2"
|
||||
exec rm -f $CTMlock
|
||||
exit 1
|
||||
}
|
||||
|
||||
puts "mkctm done"
|
||||
|
||||
if {$CTMtest} {
|
||||
puts "testing, stopping now."
|
||||
exec rm -f $CTMlock
|
||||
exit 0
|
||||
}
|
||||
if {$CTMapply} {
|
||||
puts "Applying delta"
|
||||
flush stdout
|
||||
exec echo now applying > $CTMlock
|
||||
exec sh -e -c "cd $CTMcopy ; $CTMSW/ctm -v -v -v ${CTMtmp}:${nm}.gz" >& ${CTMtmp}.apply
|
||||
exec echo did apply > $CTMlock
|
||||
}
|
||||
puts "Moving delta"
|
||||
flush stdout
|
||||
exec mv ${CTMtmp}:${nm}.gz $CTMdest/.CTMtmp_${nm}.gz >&@ stdout
|
||||
exec mv $CTMdest/.CTMtmp_${nm}.gz $CTMdest/${nm}.gz >&@ stdout
|
||||
exec echo moved > $CTMlock
|
||||
|
||||
exec sh -c "rm -rf ${CTMtmp}.*" >&@ stdout
|
||||
|
||||
if {$CTMmail != ""} {
|
||||
puts "Mailing delta"
|
||||
flush stdout
|
||||
exec $CTMSW/ctm_smail -m $CTMmaxmsg -c $CTMmaxctm $CTMdest/${nm}.gz $CTMmail >&@ stdout
|
||||
if {$CTMqueue != "" && $CTMqueuemail != ""} {
|
||||
puts "Queueing delta"
|
||||
flush stdout
|
||||
exec $CTMSW/ctm_smail -m $CTMmaxmsg -c $CTMmaxctm -q $CTMqueue $CTMdest/${nm}.gz $CTMqueuemail >&@ stdout
|
||||
puts "Sending initial two deltas"
|
||||
flush stdout
|
||||
exec $CTMSW/ctm_dequeue -n 2 $CTMqueue >&@ stdout
|
||||
}
|
||||
}
|
||||
exec echo mailed > $CTMlock
|
||||
|
||||
# If we did an absolute delta: stop.
|
||||
if {$CTMsuff != ""} break
|
||||
|
||||
# Make an absolute delta (!) every 100 deltas
|
||||
if {$CTMnbr == 0 || ($CTMnbr % 100)} break
|
||||
|
||||
# Make an absolute delta too...
|
||||
set CTMref $CTMcopy
|
||||
set CTMsuff A
|
||||
set CTMcopy ""
|
||||
set CTMmail ""
|
||||
set CTMqueue ""
|
||||
set CTMqueuemail ""
|
||||
set CTMapply 0
|
||||
set CTMspecial 1
|
||||
exec rm -f $CTMlock
|
||||
}
|
||||
puts "done."
|
||||
exec rm -f $CTMlock
|
@ -1,597 +0,0 @@
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/* Still missing:
|
||||
*
|
||||
* mkctm
|
||||
* -B regex Bogus
|
||||
* -I regex Ignore
|
||||
* -D int Damage
|
||||
* -q decrease verbosity
|
||||
* -v increase verbosity
|
||||
* -l file logfile
|
||||
* name cvs-cur
|
||||
* prefix src/secure
|
||||
* dir1 "Soll"
|
||||
* dir2 "Ist"
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <dirent.h>
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <md5.h>
|
||||
#include <err.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define DEFAULT_IGNORE "/CVS$|/\\.#|00_TRANS\\.TBL$"
|
||||
#define DEFAULT_BOGUS "\\.core$|\\.orig$|\\.rej$|\\.o$"
|
||||
regex_t reg_ignore, reg_bogus;
|
||||
int flag_ignore, flag_bogus;
|
||||
|
||||
int verbose;
|
||||
int damage, damage_limit;
|
||||
int change;
|
||||
|
||||
FILE *logf;
|
||||
|
||||
u_long s1_ignored, s2_ignored;
|
||||
u_long s1_bogus, s2_bogus;
|
||||
u_long s1_wrong, s2_wrong;
|
||||
u_long s_new_dirs, s_new_files, s_new_bytes;
|
||||
u_long s_del_dirs, s_del_files, s_del_bytes;
|
||||
u_long s_files_chg, s_bytes_add, s_bytes_del;
|
||||
u_long s_same_dirs, s_same_files, s_same_bytes;
|
||||
u_long s_edit_files, s_edit_bytes, s_edit_saves;
|
||||
u_long s_sub_files, s_sub_bytes;
|
||||
|
||||
void
|
||||
Usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: mkctm [-options] name number timestamp prefix dir1 dir2\n");
|
||||
fprintf(stderr, "options:\n");
|
||||
fprintf(stderr, "\t\t-B bogus_regexp\n");
|
||||
fprintf(stderr, "\t\t-D damage_limit\n");
|
||||
fprintf(stderr, "\t\t-I ignore_regexp\n");
|
||||
fprintf(stderr, "\t\t-q\n");
|
||||
fprintf(stderr, "\t\t-v\n");
|
||||
}
|
||||
|
||||
void
|
||||
print_stat(FILE *fd, char *pre)
|
||||
{
|
||||
fprintf(fd, "%sNames:\n", pre);
|
||||
fprintf(fd, "%s ignore: %5lu ref %5lu target\n",
|
||||
pre, s1_ignored, s2_ignored);
|
||||
fprintf(fd, "%s bogus: %5lu ref %5lu target\n",
|
||||
pre, s1_bogus, s2_bogus);
|
||||
fprintf(fd, "%s wrong: %5lu ref %5lu target\n",
|
||||
pre, s1_wrong, s2_wrong);
|
||||
fprintf(fd, "%sDelta:\n", pre);
|
||||
fprintf(fd, "%s new: %5lu dirs %5lu files %9lu plus\n",
|
||||
pre, s_new_dirs, s_new_files, s_new_bytes);
|
||||
fprintf(fd, "%s del: %5lu dirs %5lu files %9lu minus\n",
|
||||
pre, s_del_dirs, s_del_files, s_del_bytes);
|
||||
fprintf(fd, "%s chg: %5lu files %9lu plus %9lu minus\n",
|
||||
pre, s_files_chg, s_bytes_add, s_bytes_del);
|
||||
fprintf(fd, "%s same: %5lu dirs %5lu files %9lu bytes\n",
|
||||
pre, s_same_dirs, s_same_files, s_same_bytes);
|
||||
fprintf(fd, "%sMethod:\n", pre);
|
||||
fprintf(fd, "%s edit: %5lu files %9lu bytes %9lu saved\n",
|
||||
pre, s_edit_files, s_edit_bytes, s_edit_saves);
|
||||
fprintf(fd, "%s sub: %5lu files %9lu bytes\n",
|
||||
pre, s_sub_files, s_sub_bytes);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
stat_info(int foo)
|
||||
{
|
||||
signal(SIGINFO, stat_info);
|
||||
print_stat(stderr, "INFO: ");
|
||||
}
|
||||
|
||||
void DoDir(const char *dir1, const char *dir2, const char *name);
|
||||
|
||||
static struct stat st;
|
||||
static __inline struct stat *
|
||||
StatFile(char *name)
|
||||
{
|
||||
if (lstat(name, &st) < 0)
|
||||
err(1, "couldn't stat %s", name);
|
||||
return &st;
|
||||
}
|
||||
|
||||
int
|
||||
dirselect(struct dirent *de)
|
||||
{
|
||||
if (!strcmp(de->d_name, ".")) return 0;
|
||||
if (!strcmp(de->d_name, "..")) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
name_stat(const char *pfx, const char *dir, const char *name, struct dirent *de)
|
||||
{
|
||||
char *buf = alloca(strlen(dir) + strlen(name) +
|
||||
strlen(de->d_name) + 3);
|
||||
struct stat *st;
|
||||
|
||||
strcpy(buf, dir);
|
||||
strcat(buf, "/"); strcat(buf, name);
|
||||
strcat(buf, "/"); strcat(buf, de->d_name);
|
||||
st = StatFile(buf);
|
||||
printf("%s %s%s %u %u %o",
|
||||
pfx, name, de->d_name,
|
||||
st->st_uid, st->st_gid, st->st_mode & ~S_IFMT);
|
||||
fprintf(logf, "%s %s%s\n", pfx, name, de->d_name);
|
||||
if (verbose > 1) {
|
||||
fprintf(stderr, "%s %s%s\n", pfx, name, de->d_name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Equ(const char *dir1, const char *dir2, const char *name, struct dirent *de)
|
||||
{
|
||||
if (de->d_type == DT_DIR) {
|
||||
char *p = alloca(strlen(name)+strlen(de->d_name)+2);
|
||||
|
||||
strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
|
||||
DoDir(dir1, dir2, p);
|
||||
s_same_dirs++;
|
||||
} else {
|
||||
char *buf1 = alloca(strlen(dir1) + strlen(name) +
|
||||
strlen(de->d_name) + 3);
|
||||
char *buf2 = alloca(strlen(dir2) + strlen(name) +
|
||||
strlen(de->d_name) + 3);
|
||||
char *m1, md5_1[33], *m2, md5_2[33];
|
||||
u_char *p1, *p2;
|
||||
int fd1, fd2;
|
||||
struct stat s1, s2;
|
||||
|
||||
strcpy(buf1, dir1);
|
||||
strcat(buf1, "/"); strcat(buf1, name);
|
||||
strcat(buf1, "/"); strcat(buf1, de->d_name);
|
||||
fd1 = open(buf1, O_RDONLY);
|
||||
if(fd1 < 0) { err(3, "%s", buf1); }
|
||||
fstat(fd1, &s1);
|
||||
strcpy(buf2, dir2);
|
||||
strcat(buf2, "/"); strcat(buf2, name);
|
||||
strcat(buf2, "/"); strcat(buf2, de->d_name);
|
||||
fd2 = open(buf2, O_RDONLY);
|
||||
if(fd2 < 0) { err(3, "%s", buf2); }
|
||||
fstat(fd2, &s2);
|
||||
#if 0
|
||||
/* XXX if we could just trust the size to change... */
|
||||
if (s1.st_size == s2.st_size) {
|
||||
s_same_files++;
|
||||
s_same_bytes += s1.st_size;
|
||||
close(fd1);
|
||||
close(fd2);
|
||||
goto finish;
|
||||
}
|
||||
#endif
|
||||
p1=mmap(0, s1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
|
||||
if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf1); }
|
||||
close(fd1);
|
||||
|
||||
p2=mmap(0, s2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
|
||||
if (p2 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
|
||||
close(fd2);
|
||||
|
||||
/* If identical, we're done. */
|
||||
if((s1.st_size == s2.st_size) && !memcmp(p1, p2, s1.st_size)) {
|
||||
s_same_files++;
|
||||
s_same_bytes += s1.st_size;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
s_files_chg++;
|
||||
change++;
|
||||
if (s1.st_size > s2.st_size)
|
||||
s_bytes_del += (s1.st_size - s2.st_size);
|
||||
else
|
||||
s_bytes_add += (s2.st_size - s1.st_size);
|
||||
|
||||
m1 = MD5Data(p1, s1.st_size, md5_1);
|
||||
m2 = MD5Data(p2, s2.st_size, md5_2);
|
||||
|
||||
/* Just a curiosity... */
|
||||
if(!strcmp(m1, m2)) {
|
||||
if (s1.st_size != s2.st_size)
|
||||
fprintf(stderr,
|
||||
"Notice: MD5 same for files of diffent size:\n\t%s\n\t%s\n",
|
||||
buf1, buf2);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
{
|
||||
u_long l = s2.st_size + 2;
|
||||
u_char *cmd = alloca(strlen(buf1)+strlen(buf2)+100);
|
||||
u_char *ob = malloc(l), *p;
|
||||
int j;
|
||||
FILE *F;
|
||||
|
||||
if (s1.st_size && p1[s1.st_size-1] != '\n') {
|
||||
if (verbose > 0)
|
||||
fprintf(stderr,
|
||||
"last char != \\n in %s\n",
|
||||
buf1);
|
||||
goto subst;
|
||||
}
|
||||
|
||||
if (s2.st_size && p2[s2.st_size-1] != '\n') {
|
||||
if (verbose > 0)
|
||||
fprintf(stderr,
|
||||
"last char != \\n in %s\n",
|
||||
buf2);
|
||||
goto subst;
|
||||
}
|
||||
|
||||
for (p=p1; p<p1+s1.st_size; p++)
|
||||
if (!*p) {
|
||||
if (verbose > 0)
|
||||
fprintf(stderr,
|
||||
"NULL char in %s\n",
|
||||
buf1);
|
||||
goto subst;
|
||||
}
|
||||
|
||||
for (p=p2; p<p2+s2.st_size; p++)
|
||||
if (!*p) {
|
||||
if (verbose > 0)
|
||||
fprintf(stderr,
|
||||
"NULL char in %s\n",
|
||||
buf2);
|
||||
goto subst;
|
||||
}
|
||||
|
||||
strcpy(cmd, "diff -n ");
|
||||
strcat(cmd, buf1);
|
||||
strcat(cmd, " ");
|
||||
strcat(cmd, buf2);
|
||||
F = popen(cmd, "r");
|
||||
for (j = 1, l = 0; l < s2.st_size; ) {
|
||||
j = fread(ob+l, 1, s2.st_size - l, F);
|
||||
if (j < 1)
|
||||
break;
|
||||
l += j;
|
||||
continue;
|
||||
}
|
||||
if (j) {
|
||||
l = 0;
|
||||
while (EOF != fgetc(F))
|
||||
continue;
|
||||
}
|
||||
pclose(F);
|
||||
|
||||
if (l && l < s2.st_size) {
|
||||
name_stat("CTMFN", dir2, name, de);
|
||||
printf(" %s %s %d\n", m1, m2, (unsigned)l);
|
||||
fwrite(ob, 1, l, stdout);
|
||||
putchar('\n');
|
||||
s_edit_files++;
|
||||
s_edit_bytes += l;
|
||||
s_edit_saves += (s2.st_size - l);
|
||||
} else {
|
||||
subst:
|
||||
name_stat("CTMFS", dir2, name, de);
|
||||
printf(" %s %s %u\n", m1, m2, (unsigned)s2.st_size);
|
||||
fwrite(p2, 1, s2.st_size, stdout);
|
||||
putchar('\n');
|
||||
s_sub_files++;
|
||||
s_sub_bytes += s2.st_size;
|
||||
}
|
||||
free(ob);
|
||||
}
|
||||
finish:
|
||||
munmap(p1, s1.st_size);
|
||||
munmap(p2, s2.st_size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Add(const char *dir1, const char *dir2, const char *name, struct dirent *de)
|
||||
{
|
||||
change++;
|
||||
if (de->d_type == DT_DIR) {
|
||||
char *p = alloca(strlen(name)+strlen(de->d_name)+2);
|
||||
strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
|
||||
name_stat("CTMDM", dir2, name, de);
|
||||
putchar('\n');
|
||||
s_new_dirs++;
|
||||
DoDir(dir1, dir2, p);
|
||||
} else if (de->d_type == DT_REG) {
|
||||
char *buf2 = alloca(strlen(dir2) + strlen(name) +
|
||||
strlen(de->d_name) + 3);
|
||||
char *m2, md5_2[33];
|
||||
u_char *p1;
|
||||
struct stat st;
|
||||
int fd1;
|
||||
|
||||
strcpy(buf2, dir2);
|
||||
strcat(buf2, "/"); strcat(buf2, name);
|
||||
strcat(buf2, "/"); strcat(buf2, de->d_name);
|
||||
fd1 = open(buf2, O_RDONLY);
|
||||
if (fd1 < 0) { err(3, "%s", buf2); }
|
||||
fstat(fd1, &st);
|
||||
p1=mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
|
||||
if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
|
||||
close(fd1);
|
||||
m2 = MD5Data(p1, st.st_size, md5_2);
|
||||
name_stat("CTMFM", dir2, name, de);
|
||||
printf(" %s %u\n", m2, (unsigned)st.st_size);
|
||||
fwrite(p1, 1, st.st_size, stdout);
|
||||
putchar('\n');
|
||||
munmap(p1, st.st_size);
|
||||
s_new_files++;
|
||||
s_new_bytes += st.st_size;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Del (const char *dir1, const char *dir2, const char *name, struct dirent *de)
|
||||
{
|
||||
damage++;
|
||||
change++;
|
||||
if (de->d_type == DT_DIR) {
|
||||
char *p = alloca(strlen(name)+strlen(de->d_name)+2);
|
||||
strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
|
||||
DoDir(dir1, dir2, p);
|
||||
printf("CTMDR %s%s\n", name, de->d_name);
|
||||
fprintf(logf, "CTMDR %s%s\n", name, de->d_name);
|
||||
if (verbose > 1) {
|
||||
fprintf(stderr, "CTMDR %s%s\n", name, de->d_name);
|
||||
}
|
||||
s_del_dirs++;
|
||||
} else if (de->d_type == DT_REG) {
|
||||
char *buf1 = alloca(strlen(dir1) + strlen(name) +
|
||||
strlen(de->d_name) + 3);
|
||||
char *m1, md5_1[33];
|
||||
strcpy(buf1, dir1);
|
||||
strcat(buf1, "/"); strcat(buf1, name);
|
||||
strcat(buf1, "/"); strcat(buf1, de->d_name);
|
||||
m1 = MD5File(buf1, md5_1);
|
||||
printf("CTMFR %s%s %s\n", name, de->d_name, m1);
|
||||
fprintf(logf, "CTMFR %s%s %s\n", name, de->d_name, m1);
|
||||
if (verbose > 1) {
|
||||
fprintf(stderr, "CTMFR %s%s\n", name, de->d_name);
|
||||
}
|
||||
s_del_files++;
|
||||
s_del_bytes += StatFile(buf1)->st_size;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GetNext(int *i, int *n, struct dirent **nl, const char *dir, const char *name, u_long *ignored, u_long *bogus, u_long *wrong)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
char buf1[BUFSIZ];
|
||||
|
||||
for (;;) {
|
||||
for (;;) {
|
||||
(*i)++;
|
||||
if (*i >= *n)
|
||||
return;
|
||||
strcpy(buf1, name);
|
||||
if (buf1[strlen(buf1)-1] != '/')
|
||||
strcat(buf1, "/");
|
||||
strcat(buf1, nl[*i]->d_name);
|
||||
if (flag_ignore &&
|
||||
!regexec(®_ignore, buf1, 0, 0, 0)) {
|
||||
(*ignored)++;
|
||||
fprintf(logf, "Ignore %s\n", buf1);
|
||||
if (verbose > 2) {
|
||||
fprintf(stderr, "Ignore %s\n", buf1);
|
||||
}
|
||||
} else if (flag_bogus &&
|
||||
!regexec(®_bogus, buf1, 0, 0, 0)) {
|
||||
(*bogus)++;
|
||||
fprintf(logf, "Bogus %s\n", buf1);
|
||||
fprintf(stderr, "Bogus %s\n", buf1);
|
||||
damage++;
|
||||
} else {
|
||||
*buf = 0;
|
||||
if (*dir != '/')
|
||||
strcat(buf, "/");
|
||||
strcat(buf, dir);
|
||||
if (buf[strlen(buf)-1] != '/')
|
||||
strcat(buf, "/");
|
||||
strcat(buf, buf1);
|
||||
break;
|
||||
}
|
||||
free(nl[*i]); nl[*i] = 0;
|
||||
}
|
||||
/* If the filesystem didn't tell us, find type */
|
||||
if (nl[*i]->d_type == DT_UNKNOWN)
|
||||
nl[*i]->d_type = IFTODT(StatFile(buf)->st_mode);
|
||||
if (nl[*i]->d_type == DT_REG || nl[*i]->d_type == DT_DIR)
|
||||
break;
|
||||
(*wrong)++;
|
||||
if (verbose > 0)
|
||||
fprintf(stderr, "Wrong %s\n", buf);
|
||||
free(nl[*i]); nl[*i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DoDir(const char *dir1, const char *dir2, const char *name)
|
||||
{
|
||||
int i1, i2, n1, n2, i;
|
||||
struct dirent **nl1, **nl2;
|
||||
char *buf1 = alloca(strlen(dir1) + strlen(name) + 4);
|
||||
char *buf2 = alloca(strlen(dir2) + strlen(name) + 4);
|
||||
|
||||
strcpy(buf1, dir1); strcat(buf1, "/"); strcat(buf1, name);
|
||||
strcpy(buf2, dir2); strcat(buf2, "/"); strcat(buf2, name);
|
||||
n1 = scandir(buf1, &nl1, dirselect, alphasort);
|
||||
n2 = scandir(buf2, &nl2, dirselect, alphasort);
|
||||
i1 = i2 = -1;
|
||||
GetNext(&i1, &n1, nl1, dir1, name, &s1_ignored, &s1_bogus, &s1_wrong);
|
||||
GetNext(&i2, &n2, nl2, dir2, name, &s2_ignored, &s2_bogus, &s2_wrong);
|
||||
for (;i1 < n1 || i2 < n2;) {
|
||||
|
||||
if (damage_limit && damage > damage_limit)
|
||||
break;
|
||||
|
||||
/* Get next item from list 1 */
|
||||
if (i1 < n1 && !nl1[i1])
|
||||
GetNext(&i1, &n1, nl1, dir1, name,
|
||||
&s1_ignored, &s1_bogus, &s1_wrong);
|
||||
|
||||
/* Get next item from list 2 */
|
||||
if (i2 < n2 && !nl2[i2])
|
||||
GetNext(&i2, &n2, nl2, dir2, name,
|
||||
&s2_ignored, &s2_bogus, &s2_wrong);
|
||||
|
||||
if (i1 >= n1 && i2 >= n2) {
|
||||
/* Done */
|
||||
break;
|
||||
} else if (i1 >= n1 && i2 < n2) {
|
||||
/* end of list 1, add anything left on list 2 */
|
||||
Add(dir1, dir2, name, nl2[i2]);
|
||||
free(nl2[i2]); nl2[i2] = 0;
|
||||
} else if (i1 < n1 && i2 >= n2) {
|
||||
/* end of list 2, delete anything left on list 1 */
|
||||
Del(dir1, dir2, name, nl1[i1]);
|
||||
free(nl1[i1]); nl1[i1] = 0;
|
||||
} else if (!(i = strcmp(nl1[i1]->d_name, nl2[i2]->d_name))) {
|
||||
/* Identical names */
|
||||
if (nl1[i1]->d_type == nl2[i2]->d_type) {
|
||||
/* same type */
|
||||
Equ(dir1, dir2, name, nl1[i1]);
|
||||
} else {
|
||||
/* different types */
|
||||
Del(dir1, dir2, name, nl1[i1]);
|
||||
Add(dir1, dir2, name, nl2[i2]);
|
||||
}
|
||||
free(nl1[i1]); nl1[i1] = 0;
|
||||
free(nl2[i2]); nl2[i2] = 0;
|
||||
} else if (i < 0) {
|
||||
/* Something extra in list 1, delete it */
|
||||
Del(dir1, dir2, name, nl1[i1]);
|
||||
free(nl1[i1]); nl1[i1] = 0;
|
||||
} else {
|
||||
/* Something extra in list 2, add it */
|
||||
Add(dir1, dir2, name, nl2[i2]);
|
||||
free(nl2[i2]); nl2[i2] = 0;
|
||||
}
|
||||
}
|
||||
if (n1 >= 0)
|
||||
free(nl1);
|
||||
if (n2 >= 0)
|
||||
free(nl2);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
setbuf(stderr, NULL);
|
||||
|
||||
#if 0
|
||||
if (regcomp(®_bogus, DEFAULT_BOGUS, REG_EXTENDED | REG_NEWLINE))
|
||||
/* XXX use regerror to explain it */
|
||||
errx(1, "default regular expression argument to -B is botched");
|
||||
flag_bogus = 1;
|
||||
|
||||
if (regcomp(®_ignore, DEFAULT_IGNORE, REG_EXTENDED | REG_NEWLINE))
|
||||
/* XXX use regerror to explain it */
|
||||
errx(1, "default regular expression argument to -I is botched");
|
||||
flag_ignore = 1;
|
||||
#endif
|
||||
|
||||
while ((i = getopt(argc, argv, "D:I:B:l:qv")) != -1)
|
||||
switch (i) {
|
||||
case 'D':
|
||||
damage_limit = strtol(optarg, 0, 0);
|
||||
if (damage_limit < 0)
|
||||
errx(1, "damage limit must be positive");
|
||||
break;
|
||||
case 'I':
|
||||
if (flag_ignore)
|
||||
regfree(®_ignore);
|
||||
flag_ignore = 0;
|
||||
if (!*optarg)
|
||||
break;
|
||||
if (regcomp(®_ignore, optarg,
|
||||
REG_EXTENDED | REG_NEWLINE))
|
||||
/* XXX use regerror to explain it */
|
||||
errx(1, "regular expression argument to -I is botched");
|
||||
flag_ignore = 1;
|
||||
break;
|
||||
case 'B':
|
||||
if (flag_bogus)
|
||||
regfree(®_bogus);
|
||||
flag_bogus = 0;
|
||||
if (!*optarg)
|
||||
break;
|
||||
if (regcomp(®_bogus, optarg,
|
||||
REG_EXTENDED | REG_NEWLINE))
|
||||
/* XXX use regerror to explain it */
|
||||
errx(1, "regular expression argument to -B is botched");
|
||||
flag_bogus = 1;
|
||||
break;
|
||||
case 'l':
|
||||
logf = fopen(optarg, "w");
|
||||
if (!logf)
|
||||
err(1, "%s", optarg);
|
||||
setlinebuf(logf);
|
||||
break;
|
||||
case 'q':
|
||||
verbose--;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
Usage();
|
||||
return (1);
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (!logf)
|
||||
logf = fopen(_PATH_DEVNULL, "w");
|
||||
|
||||
setbuf(stdout, 0);
|
||||
|
||||
if (argc != 6) {
|
||||
Usage();
|
||||
return (1);
|
||||
}
|
||||
|
||||
signal(SIGINFO, stat_info);
|
||||
|
||||
fprintf(stderr, "CTM_BEGIN 2.0 %s %s %s %s\n",
|
||||
argv[0], argv[1], argv[2], argv[3]);
|
||||
fprintf(logf, "CTM_BEGIN 2.0 %s %s %s %s\n",
|
||||
argv[0], argv[1], argv[2], argv[3]);
|
||||
printf("CTM_BEGIN 2.0 %s %s %s %s\n",
|
||||
argv[0], argv[1], argv[2], argv[3]);
|
||||
DoDir(argv[4], argv[5], "");
|
||||
if (damage_limit && damage > damage_limit) {
|
||||
print_stat(stderr, "DAMAGE: ");
|
||||
errx(1, "damage of %d would exceed %d files",
|
||||
damage, damage_limit);
|
||||
} else if (change < 2) {
|
||||
errx(4, "no changes");
|
||||
} else {
|
||||
printf("CTM_END ");
|
||||
fprintf(logf, "CTM_END\n");
|
||||
print_stat(stderr, "END: ");
|
||||
}
|
||||
exit(0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user