Hello,
Attached is a patch that extends the functionality of Coreutils
install creating a basic package management capability. If it is to be
considered useful or viable, I would like very much to receive
reveiews, suggestions for improvements or bug reports. My purpose is
to get the code integrated to Coreutils 'install'.
Thank you,
Joao Luis.
** RATIONALE **
When installing programs from upstream source code, 'install' is the
traditional way makefiles install files to /usr/local or other
locations. After that, it is difficult to get a full listing of what
was installed where, and usually one must keep the source code so it
is possible to 'make uninstall' in the future, in case the makefile
provides this possibility.
This patch allows 'install' to make a list of all installed files and
created directories, making it easy to track all files and uninstall
the "package".
** USAGE **
With this patch, when 'install' is executed with the command line options
-l FILE
or
--list=FILE
it will append to FILE the canonicalized name of the installed file or
directory.
For example
install -l file_list -D my_file /usr/local/my_dir/my_file
or
install --list=file_list -D my_file /usr/local/my_dir/my_file
or
export INSTALL_LIST=file_list
install -D my_file /usr/local/my_dir/my_file
will append to file_list (or create it if it doesn't exist) containing
/usr/local/my_dir
/usr/local/my_dir/my_file
After that, files and directories may be easily uninstalled with
tac file_list | while read f; do test -d "$f" && \rmdir "$f" || \rm
"$f"; done; \rm file_list
** USE CASES **
Install may be called in new makefiles with the option -l to generate
a list of installed files and direcory. For legacy makefiles, one can
execute
INSTALL_LIST=somefile make install
or
export INSTALL_LIST=somefile
make install
or the like.
** DETAILS **
A global variable
static FILE *install_list = NULL;
was created to hold the stream for the file list. This stream is
fopen()ed for append in main() and closed by the atexit() handler
close_install_list().
A function
static void install_list_append (const char *fname)
was created to write to the file list. This function is called by:
make_ancestor(), to record all the created parent directories (case of
option -D)
process_dir(), to record a created dir (case of option -d)
install_file_in_file(), to record installed files.
--- src/install.c.ORIG 2016-07-28 15:01:31.115348097 -0300
+++ src/install.c 2016-07-28 20:52:18.193822263 -0300
@@ -18,6 +18,7 @@
#include <config.h>
#include <stdio.h>
+#include <stdlib.h>
#include <getopt.h>
#include <sys/types.h>
#include <signal.h>
@@ -28,6 +29,7 @@
#include "system.h"
#include "backupfile.h"
+#include "canonicalize.h"
#include "error.h"
#include "cp-hash.h"
#include "copy.h"
@@ -109,6 +111,9 @@
/* Program used to strip binaries, "strip" is default */
static char const *strip_program = "strip";
+/* Stream to record installed files and directories */
+static FILE *install_list = NULL;
+
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
@@ -124,6 +129,7 @@
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{"directory", no_argument, NULL, 'd'},
{"group", required_argument, NULL, 'g'},
+ {"list", required_argument, NULL, 'l'},
{"mode", required_argument, NULL, 'm'},
{"no-target-directory", no_argument, NULL, 'T'},
{"owner", required_argument, NULL, 'o'},
@@ -409,6 +415,20 @@
return is_a_dir;
}
+/* Append fname to the install list */
+
+static void
+install_list_append (const char *fname)
+{
+ char *fname_canon;
+ if (install_list == NULL)
+ return;
+ fname_canon = canonicalize_file_name (fname);
+ if (fprintf (install_list, "%s\n", fname_canon) < 0)
+ error (EXIT_FAILURE, errno, _("cannot write to install list"));
+ free (fname_canon);
+}
+
/* Report that directory DIR was made, if OPTIONS requests this. */
static void
announce_mkdir (char const *dir, void *options)
@@ -432,7 +452,10 @@
int r = mkdir (component, DEFAULT_MODE);
if (r == 0)
- announce_mkdir (dir, options);
+ {
+ install_list_append (component);
+ announce_mkdir (dir, options);
+ }
return r;
}
@@ -448,6 +471,10 @@
? EXIT_SUCCESS
: EXIT_FAILURE);
+ /* After patch is reviewed and accepted, integrate with if below */
+ if (ret == EXIT_SUCCESS)
+ install_list_append (dir);
+
/* FIXME: Due to the current structure of make_dir_parents()
we don't have the facility to call defaultcon() before the
final component of DIR is created. So for now, create the
@@ -658,6 +685,9 @@
or all components of --target-directory,\n\
then copy SOURCE to DEST\n\
-g, --group=GROUP set group ownership, instead of process' current group\n\
+ -l, --list=FILE append to FILE the installed file or directory names;\n\
+ FILE may be passed also through the environment\n\
+ variable INSTALL_LIST\n\
-m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\
-o, --owner=OWNER set ownership (super-user only)\n\
"), stdout);
@@ -715,6 +745,7 @@
}
if (! copy_file (from, to, x))
return false;
+ install_list_append (to);
if (strip_files)
if (! strip (to))
{
@@ -800,6 +831,12 @@
return ret;
}
+static void close_install_list (void)
+{
+ if (install_list && fclose (install_list) == EOF)
+ error (0, errno, _("cannot write to install list"));
+}
+
int
main (int argc, char **argv)
{
@@ -817,6 +854,7 @@
char **file;
bool strip_program_specified = false;
char const *scontext = NULL;
+ char *install_list_fname = NULL;
/* set iff kernel has extra selinux system calls */
selinux_enabled = (0 < is_selinux_enabled ());
@@ -827,6 +865,7 @@
textdomain (PACKAGE);
atexit (close_stdin);
+ atexit (close_install_list);
cp_option_init (&x);
@@ -840,8 +879,8 @@
we'll actually use backup_suffix_string. */
backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
- while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z", long_options,
- NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "bcCsDdg:l:m:o:pt:TvS:Z",
+ long_options, NULL)) != -1)
{
switch (optc)
{
@@ -878,6 +917,9 @@
case 'g':
group_name = optarg;
break;
+ case 'l':
+ install_list_fname = optarg;
+ break;
case 'm':
specified_mode = optarg;
break;
@@ -1012,6 +1054,13 @@
quoteaf (file[n_files - 1]));
}
+ if (install_list_fname || (install_list_fname = getenv ("INSTALL_LIST")))
+ {
+ install_list = fopen (install_list_fname, "a");
+ if (install_list == NULL)
+ error (EXIT_FAILURE, errno, _("failed to open %s"), install_list_fname);
+ }
+
if (specified_mode)
{
struct mode_change *change = mode_compile (specified_mode);