Files
nvidia-installer/command-list.c
Aaron Plattner 6765fcac47 346.16
2014-11-13 08:51:28 -08:00

1033 lines
29 KiB
C

/*
* nvidia-installer: A tool for installing NVIDIA software packages on
* Unix and Linux systems.
*
* Copyright (C) 2003 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>.
*
*
* command_list.c - this source file contains functions for building
* and executing a commandlist (the list of operations to perform to
* actually do an install).
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <fts.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include "nvidia-installer.h"
#include "command-list.h"
#include "user-interface.h"
#include "backup.h"
#include "misc.h"
#include "files.h"
#include "kernel.h"
#include "manifest.h"
static void free_file_list(FileList* l);
static void find_conflicting_kernel_modules(Options *op,
Package *p,
FileList *l);
static void find_existing_files(Package *p, FileList *l,
PackageEntryFileTypeList *file_type_list);
static void condense_file_list(Package *p, FileList *l);
static void add_command (CommandList *c, int cmd, ...);
static void add_file_to_list(const char*, const char*, FileList*);
static void append_to_rpm_file_list(Options *op, Command *c);
/*
* find_conflicting_files() optionally takes an array of NoRecursionDirectory
* entries to indicate which directories should not be recursively searched.
*/
typedef struct {
int level; /* max search depth: set to negative for unrestricted depth */
char *name; /* name to find: NULL to end the list */
} NoRecursionDirectory;
static void find_conflicting_files(Options *op,
char *path,
ConflictingFileInfo *files,
FileList *l,
const NoRecursionDirectory *skipdirs);
/*
* Check if a path already exists in the path list, or is a subdirectory of
* a path that exists in the path list, or is a symlink to or symlink target
* of a directory that exists in the path list.
*/
static int path_already_exists(char ***paths, int count, const char *path)
{
int i;
for (i = 0; i < count; i++) {
int is_subdir = FALSE;
is_subdirectory((*paths)[i], path, &is_subdir);
if (is_subdir) {
return TRUE;
}
}
return FALSE;
}
/*
* Add a new path to the list of paths to search, provided that it exists
* and is not redundant.
* XXX we only check to see if the new directory is a subdirectory of any
* existing directory, and not the other way around.
*/
static void add_search_path(char ***paths, int *count, const char *path)
{
if (directory_exists(path) && !path_already_exists(paths, *count, path)) {
*paths = nvrealloc(*paths, sizeof(char *) * (*count + 1));
(*paths)[*count] = nvstrdup(path);
(*count)++;
}
}
/*
* Given a path, add the subdirectories "/lib", "/lib32", and "/lib64" to the
* list of paths to search.
*/
static void add_search_paths(char ***paths, int *count, const char *pathbase)
{
int i;
const char *subdirs[] = {
"/lib",
"/lib32",
"/lib64",
};
for (i = 0; i < ARRAY_LEN(subdirs); i++) {
char *path = nvstrcat(pathbase, subdirs[i], NULL);
add_search_path(paths, count, path);
nvfree(path);
}
}
/*
* Build the list of paths under which to search for conflicting files.
* Returns the number of paths added to the search list.
*/
static int get_conflicting_search_paths(const Options *op, char ***paths)
{
int ret = 0;
*paths = NULL;
add_search_paths(paths, &ret, DEFAULT_X_PREFIX);
add_search_paths(paths, &ret, XORG7_DEFAULT_X_PREFIX);
add_search_paths(paths, &ret, op->x_prefix);
add_search_paths(paths, &ret, DEFAULT_OPENGL_PREFIX);
add_search_paths(paths, &ret, op->opengl_prefix);
add_search_path(paths, &ret, op->x_module_path);
add_search_path(paths, &ret, op->x_library_path);
#if defined(NV_X86_64)
if (op->compat32_chroot != NULL) {
int i;
char *subdirs[] = {
DEFAULT_X_PREFIX,
op->x_prefix,
DEFAULT_OPENGL_PREFIX,
op->opengl_prefix,
op->compat32_prefix,
};
for (i = 0; i < ARRAY_LEN(subdirs); i++) {
char *path = nvstrcat(op->compat32_chroot, "/", subdirs[i], NULL);
add_search_paths(paths, &ret, path);
nvfree(path);
}
}
#endif
return ret;
}
/*
* build_command_list() - construct a list of all the things to do
* during the installation. This consists of:
*
* - backing and removing up conflicting files
* - installing all the installable files
* - create any needed symbolic links
* - executing any nessary commands
* - running `ldconfig` and `depmod -aq`
*
* If we are only installing the kernel module, then we trim back all
* the stuff that we don't need to do.
*/
CommandList *build_command_list(Options *op, Package *p)
{
FileList *l;
CommandList *c;
int i, cmd;
PackageEntryFileTypeList installable_files;
PackageEntryFileTypeList tmp_installable_files;
char *tmp;
get_installable_file_type_list(op, &installable_files);
l = (FileList *) nvalloc(sizeof(FileList));
c = (CommandList *) nvalloc(sizeof(CommandList));
/* find any possibly conflicting modules and/or libraries */
if (!op->no_kernel_module || op->dkms)
find_conflicting_kernel_modules(op, p, l);
/* check the conflicting file list for any installed kernel modules */
if (op->kernel_module_only) {
if (dkms_module_installed(op, p->version)) {
ui_error(op, "A DKMS kernel module with version %s is already "
"installed.", p->version);
free_file_list(l);
free_command_list(op,c);
return NULL;
}
for (i = 0; i < l->num; i++) {
if (find_installed_file(op, l->filename[i])) {
ui_error(op, "The file '%s' already exists as part of this "
"driver installation.", l->filename[i]);
free_file_list(l);
free_command_list(op, c);
return NULL;
}
}
/* XXX: If installing with --kernel-module-only on a system that has
* the kernel module sources already installed, but does NOT have a
* built kernel module or DKMS module, duplicate entries for the source
* files will be added to the backup log, leading to error messages
* when uninstalling the driver later leads to redundant attempts to
* delete the files. */
}
if (!op->kernel_module_only) {
char **paths;
int numpaths, i;
/*
* stop recursing into any "nvidia-cg-toolkit"
* directory to prevent libGL.so.1 from being deleted
* (see bug 843595).
*/
static const NoRecursionDirectory skipdirs[] = {
{ -1, "nvidia-cg-toolkit" },
{ 0, NULL }
};
numpaths = get_conflicting_search_paths(op, &paths);
ui_status_begin(op, "Searching for conflicting files:", "Searching");
for (i = 0; i < numpaths; i++) {
ui_status_update(op, (i + 1.0f) / numpaths, "Searching: %s", paths[i]);
find_conflicting_files(op, paths[i], p->conflicting_files, l,
skipdirs);
}
ui_status_end(op, "done.");
}
/*
* find any existing files that clash with what we're going to
* install
*/
tmp_installable_files = installable_files;
add_symlinks_to_file_type_list(&tmp_installable_files);
find_existing_files(p, l, &tmp_installable_files);
/* condense the file list */
condense_file_list(p, l);
/*
* all of the files in the conflicting file list should be backed
* up or deleted
*/
cmd = op->no_backup ? DELETE_CMD : BACKUP_CMD;
for (i = 0; i < l->num; i++)
add_command(c, cmd, l->filename[i], NULL, 0);
/* Add all the installable files to the list */
for (i = 0; i < p->num_entries; i++) {
/*
* Install first, then run execstack. This sets the selinux context on
* the installed file in the target filesystem, which is essentially
* guaranteed to support selinux attributes if selinux is enabled.
* However, the temporary filesystem containing the uninstalled file
* may be on a filesystem that doesn't support selinux attributes,
* such as NFS.
*
* Since execstack also changes the file on disk, we need to run it
* after the file is installed but before we compute and log its CRC in
* the backup log. To achieve that, we tell INTALL_CMD to run execstack
* as a post-install step.
*
* See bugs 530083 and 611327
*/
if (op->selinux_enabled &&
(op->utils[EXECSTACK] != NULL) &&
(p->entries[i].caps.is_shared_lib)) {
tmp = nvstrcat(op->utils[EXECSTACK], " -c ",
p->entries[i].dst, NULL);
} else {
tmp = NULL;
}
if (installable_files.types[p->entries[i].type]) {
add_command(c, INSTALL_CMD,
p->entries[i].file,
p->entries[i].dst,
tmp,
p->entries[i].mode);
}
nvfree(tmp);
/*
* delete any temporary generated files
*/
if (p->entries[i].caps.is_temporary) {
add_command(c, DELETE_CMD,
p->entries[i].file);
}
if (op->selinux_enabled &&
(p->entries[i].caps.is_shared_lib)) {
tmp = nvstrcat(op->utils[CHCON], " -t ", op->selinux_chcon_type,
" ", p->entries[i].dst, NULL);
add_command(c, RUN_CMD, tmp);
nvfree(tmp);
}
}
/* create any needed symbolic links */
for (i = 0; i < p->num_entries; i++) {
if (p->entries[i].caps.is_symlink) {
/* if it's a NEWSYM and the file already exists, don't add a command
* for it */
if (p->entries[i].type == FILE_TYPE_XMODULE_NEWSYM) {
struct stat buf;
if(!stat(p->entries[i].dst, &buf) || errno != ENOENT) {
ui_expert(op, "Not creating a symlink from %s to %s "
"because a file already exists at that path "
"or the path is inaccessible.",
p->entries[i].dst, p->entries[i].target);
continue;
}
}
add_command(c, SYMLINK_CMD, p->entries[i].dst,
p->entries[i].target);
}
}
/* find any commands we should run */
for (i = 0; i < p->num_entries; i++) {
if (p->entries[i].type == FILE_TYPE_KERNEL_MODULE_CMD) {
add_command(c, RUN_CMD, p->entries[i].file, NULL, 0);
}
}
/*
* if "--no-abi-note" was requested, scan for any OpenGL
* libraries, and run the following command on them:
*
* objcopy --remove-section=.note.ABI-tag <filename> 2> /dev/null ; true
*/
if (op->no_abi_note) {
if (op->utils[OBJCOPY]) {
for (i = 0; i < p->num_entries; i++) {
if (p->entries[i].type == FILE_TYPE_OPENGL_LIB) {
tmp = nvstrcat(op->utils[OBJCOPY],
" --remove-section=.note.ABI-tag ",
p->entries[i].dst,
" 2> /dev/null ; true", NULL);
add_command(c, RUN_CMD, tmp);
nvfree(tmp);
}
}
} else {
ui_warn(op, "--no-abi-note option was specified but the system "
"utility `objcopy` (package 'binutils') was not found; this "
"operation will be skipped.");
}
}
/* finally, run ldconfig and depmod */
add_command(c, RUN_CMD, op->utils[LDCONFIG]);
/*
* If we are building for a non-running kernel, specify that
* kernel name on the depmod commandline line (see depmod(8)
* manpage for details). Patch provided by Nigel Spowage
* <Nigel.Spowage@energis.com>
*/
if (!op->no_kernel_module) {
tmp = nvstrcat(op->utils[DEPMOD], " -aq ", op->kernel_name, NULL);
add_command(c, RUN_CMD, tmp);
nvfree(tmp);
}
/* free the FileList */
free_file_list(l);
return c;
} /* build_command_list() */
/*
* free_file_list() - free the file list
*/
static void free_file_list(FileList* l)
{
int i;
if (!l) return;
for (i = 0; i < l->num; i++) {
nvfree(l->filename[i]);
}
nvfree((char *) l->filename);
nvfree((char *) l);
} /* free_file_list() */
/*
* free_command_list() - free the specified commandlist
*/
void free_command_list(Options *op, CommandList *cl)
{
int i;
Command *c;
if (!cl) return;
for (i = 0; i < cl->num; i++) {
c = &cl->cmds[i];
if (c->s0) free(c->s0);
if (c->s1) free(c->s1);
if (c->s2) free(c->s2);
}
if (cl->cmds) free(cl->cmds);
free(cl);
} /* free_command_list() */
/*
* execute_run_command() - execute a RUN_CMD from the command list.
*/
static inline int execute_run_command(Options *op, float percent, const char *cmd)
{
int ret;
char *data;
ui_expert(op, "Executing: %s", cmd);
ui_status_update(op, percent, "Executing: `%s` "
"(this may take a moment...)", cmd);
ret = run_command(op, cmd, &data, TRUE, 0, TRUE);
if (ret != 0) {
ui_error(op, "Failed to execute `%s`: %s", cmd, data);
ret = continue_after_error(op, "Failed to execute `%s`", cmd);
if (!ret) {
nvfree(data);
return FALSE;
}
}
if (data) free(data);
return TRUE;
} /* execute_run_command() */
/*
* execute_command_list() - execute the commands in the command list.
*
* If any failure occurs, ask the user if they would like to continue.
*/
int execute_command_list(Options *op, CommandList *c,
const char *title, const char *msg)
{
int i, ret;
float percent;
ui_status_begin(op, title, "%s", msg);
for (i = 0; i < c->num; i++) {
percent = (float) i / (float) c->num;
switch (c->cmds[i].cmd) {
case INSTALL_CMD:
ui_expert(op, "Installing: %s --> %s",
c->cmds[i].s0, c->cmds[i].s1);
ui_status_update(op, percent, "Installing: %s", c->cmds[i].s1);
ret = install_file(op, c->cmds[i].s0, c->cmds[i].s1,
c->cmds[i].mode);
if (!ret) {
ret = continue_after_error(op, "Cannot install %s",
c->cmds[i].s1);
if (!ret) return FALSE;
} else {
/*
* perform post-install step before logging the backup
*/
if (c->cmds[i].s2 &&
!execute_run_command(op, percent, c->cmds[i].s2)) {
return FALSE;
}
log_install_file(op, c->cmds[i].s1);
append_to_rpm_file_list(op, &c->cmds[i]);
}
break;
case RUN_CMD:
if (!execute_run_command(op, percent, c->cmds[i].s0)) {
return FALSE;
}
break;
case SYMLINK_CMD:
ui_expert(op, "Creating symlink: %s -> %s",
c->cmds[i].s0, c->cmds[i].s1);
ui_status_update(op, percent, "Creating symlink: %s",
c->cmds[i].s1);
ret = install_symlink(op, c->cmds[i].s1, c->cmds[i].s0);
if (!ret) {
ret = continue_after_error(op, "Cannot create symlink %s (%s)",
c->cmds[i].s0, strerror(errno));
if (!ret) return FALSE;
} else {
log_create_symlink(op, c->cmds[i].s0, c->cmds[i].s1);
}
break;
case BACKUP_CMD:
ui_expert(op, "Backing up: %s", c->cmds[i].s0);
ui_status_update(op, percent, "Backing up: %s", c->cmds[i].s0);
ret = do_backup(op, c->cmds[i].s0);
if (!ret) {
ret = continue_after_error(op, "Cannot backup %s",
c->cmds[i].s0);
if (!ret) return FALSE;
}
break;
case DELETE_CMD:
ui_expert(op, "Deleting: %s", c->cmds[i].s0);
ret = unlink(c->cmds[i].s0);
if (ret == -1) {
ret = continue_after_error(op, "Cannot delete %s",
c->cmds[i].s0);
if (!ret) return FALSE;
}
break;
default:
/* XXX should never get here */
return FALSE;
break;
}
}
ui_status_end(op, "done.");
return TRUE;
} /* execute_command_list() */
/*
***************************************************************************
* local static routines
***************************************************************************
*/
/*
* find_conflicting_kernel_modules() - search for conflicting kernel
* modules under the kernel module installation prefix.
*/
static void find_conflicting_kernel_modules(Options *op,
Package *p, FileList *l)
{
int i = 0, n = 0;
ConflictingFileInfo files[2];
char *paths[3];
char *tmp = get_kernel_name(op);
/* Don't descend into the "build" or "source" directories; these won't
* contain modules, and may be symlinks back to an actual source tree. */
static const NoRecursionDirectory skipdirs[] = {
{ 1, "build" },
{ 1, "source" },
{ 0, NULL }
};
memset(files, 0, sizeof(files));
files[1].name = NULL;
files[1].len = 0;
if (op->kernel_module_installation_path) {
paths[i++] = op->kernel_module_installation_path;
}
if (tmp) {
paths[i++] = nvstrcat("/lib/modules/", tmp, NULL);
}
paths[i] = NULL;
for (i = 0; paths[i]; i++) {
for (n = 0; p->bad_module_filenames[n]; n++) {
/*
* Recursively search for this conflicting kernel module
* relative to the current prefix.
*/
files[0].name = p->bad_module_filenames[n];
files[0].len = strlen(files[0].name);
find_conflicting_files(op, paths[i], files, l, skipdirs);
}
}
/* free any paths we nvstrcat()'d above */
for (i = 1; paths[i]; i++) {
nvfree(paths[i]);
}
} /* find_conflicting_kernel_modules() */
/*
* find_existing_files() - given a Package description, search for any
* of the package files which already exist, and add them to the
* FileList.
*/
static void find_existing_files(Package *p, FileList *l,
PackageEntryFileTypeList *file_type_list)
{
int i;
struct stat stat_buf;
for (i = 0; i < p->num_entries; i++) {
if (file_type_list->types[p->entries[i].type]) {
if (lstat(p->entries[i].dst, &stat_buf) == 0) {
add_file_to_list(NULL, p->entries[i].dst, l);
}
}
}
} /* find_existing_files() */
/*
* ignore_conflicting_file() - ignore (i.e., do not put it on the list
* of files to backup) the conflicting file 'filename' if requiredString
* is non-NULL and we cannot find the string in 'filename', or if the
* file only conflicts on specific architectures, and the file's
* architecture does not match.
*/
static int ignore_conflicting_file(Options *op,
const char *filename,
const ConflictingFileInfo info)
{
int fd = -1;
struct stat stat_buf;
char *file = MAP_FAILED;
int ret = FALSE;
int i, len, size = 0;
/* if no requiredString, do not check for the required string */
if (!info.requiredString) return ret;
ret = FALSE;
if ((fd = open(filename, O_RDONLY)) == -1) {
ui_error(op, "Unable to open '%s' for reading (%s)",
filename, strerror(errno));
goto cleanup;
}
if (fstat(fd, &stat_buf) == -1) {
ui_error(op, "Unable to determine size of '%s' (%s)",
filename, strerror(errno));
goto cleanup;
}
size = stat_buf.st_size;
if (!size) {
goto cleanup;
}
if ((file = mmap(0, size, PROT_READ,
MAP_FILE | MAP_SHARED, fd, 0)) == MAP_FAILED) {
ui_error(op, "Unable to map file '%s' for reading (%s)",
filename, strerror(errno));
goto cleanup;
}
/*
* if the requiredString is not found within the mapping of file,
* ignore the conflicting file; when scanning for the string,
* ensure that the string is either at the end of the file, or
* followed by '\0'.
*/
ret = TRUE;
len = strlen(info.requiredString);
for (i = 0; (i + len) <= size; i++) {
if ((strncmp(&file[i], info.requiredString, len) == 0) &&
(((i + len) == size) || (file[i+len] == '\0'))) {
ret = FALSE;
break;
}
}
/* fall through to cleanup */
cleanup:
if (file != MAP_FAILED) {
munmap(file, size);
}
if (fd != -1) {
close(fd);
}
return ret;
} /* ignore_conflicting_file() */
/*
* find_conflicting_files() - search for any conflicting
* files in all the specified paths within the hierarchy under
* the given prefix.
*/
static void find_conflicting_files(Options *op,
char *path,
ConflictingFileInfo *files,
FileList *l,
const NoRecursionDirectory *skipdirs)
{
int i;
char *paths[2];
FTS *fts;
FTSENT *ent;
paths[0] = path; /* search root */
paths[1] = NULL;
fts = fts_open(paths, FTS_LOGICAL | FTS_NOSTAT, NULL);
if (!fts) return;
while ((ent = fts_read(fts)) != NULL) {
switch (ent->fts_info) {
case FTS_F:
case FTS_SLNONE:
for (i = 0; files[i].name; i++) {
/* end compare at len e.g. so "libGL." matches "libGL.so.1" */
if (!strncmp(ent->fts_name, files[i].name, files[i].len) &&
!ignore_conflicting_file(op, ent->fts_path,
files[i])) {
add_file_to_list(NULL, ent->fts_path, l);
}
}
break;
case FTS_DP:
case FTS_D:
if (op->no_recursion) {
fts_set(fts, ent, FTS_SKIP);
} else if (skipdirs) {
const NoRecursionDirectory *dir;
for (dir = skipdirs; dir->name; dir++) {
if ((dir->level < 0 || dir->level >= ent->fts_level) &&
strcmp(ent->fts_name, dir->name) == 0) {
fts_set(fts, ent, FTS_SKIP);
}
}
}
break;
default:
/*
* we only care about regular files, symbolic links
* and directories; traversing the hierarchy logically
* to simplify handling of paths with symbolic links
* to directories, we only need to handle broken links
* and, if recursion was not disabled, directories.
*/
break;
}
}
fts_close(fts);
} /* find_conflicting_files() */
/*
* condense_file_list() - Take a FileList stucture and delete any
* duplicate entries in the list. This is a pretty brain dead
* brute-force algorithm.
*/
static void condense_file_list(Package *p, FileList *l)
{
char **s = NULL;
int n = 0, i, j, keep;
struct stat stat_buf, *stat_bufs;
/* allocate enough space in our temporary 'stat' array */
if (l->num) {
stat_bufs = nvalloc(sizeof(struct stat) * l->num);
} else {
stat_bufs = NULL;
}
/*
* walk through our original (uncondensed) list of files and move
* unique files to a new (condensed) list. For each file in the
* original list, get the filesystem information for the file, and
* then compare that to the filesystem information for all the
* files in the new list. If the file from the original list does
* not match any file in the new list, add it to the new list.
*/
for (i = 0; i < l->num; i++) {
keep = TRUE;
if (lstat(l->filename[i], &stat_buf) == -1)
continue;
/*
* check if this file is in the package we're trying to
* install; we don't want to remove files that are in the
* package; symlinks may have tricked us into looking for
* conflicting files inside our unpacked .run file.
*/
for (j = 0; j < p->num_entries; j++) {
if ((p->entries[j].device == stat_buf.st_dev) &&
(p->entries[j].inode == stat_buf.st_ino)) {
keep = FALSE;
break;
}
}
for (j = 0; keep && (j < n); j++) {
/*
* determine if the two files are the same by comparing
* device and inode
*/
if ((stat_buf.st_dev == stat_bufs[j].st_dev) &&
(stat_buf.st_ino == stat_bufs[j].st_ino)) {
keep = FALSE;
break;
}
}
if (keep) {
s = (char **) nvrealloc(s, sizeof(char *) * (n + 1));
s[n] = nvstrdup(l->filename[i]);
stat_bufs[n] = stat_buf;
n++;
}
}
if (stat_bufs) nvfree((void *)stat_bufs);
for (i = 0; i < l->num; i++) free(l->filename[i]);
free(l->filename);
l->filename = s;
l->num = n;
} /* condense_file_list() */
/*
* add_command() - grow the commandlist and append the new command,
* parsing the variable argument list.
*/
static void add_command(CommandList *c, int cmd, ...)
{
int n = c->num;
char *s;
va_list ap;
c->cmds = (Command *) nvrealloc(c->cmds, sizeof(Command) * (n + 1));
c->cmds[n].cmd = cmd;
c->cmds[n].s0 = NULL;
c->cmds[n].s1 = NULL;
c->cmds[n].s2 = NULL;
c->cmds[n].mode = 0x0;
va_start(ap, cmd);
switch (cmd) {
case INSTALL_CMD:
s = va_arg(ap, char *);
c->cmds[n].s0 = nvstrdup(s);
s = va_arg(ap, char *);
c->cmds[n].s1 = nvstrdup(s);
s = va_arg(ap, char *);
c->cmds[n].s2 = nvstrdup(s);
c->cmds[n].mode = va_arg(ap, mode_t);
break;
case BACKUP_CMD:
s = va_arg(ap, char *);
c->cmds[n].s0 = nvstrdup(s);
break;
case RUN_CMD:
s = va_arg(ap, char *);
c->cmds[n].s0 = nvstrdup(s);
break;
case SYMLINK_CMD:
s = va_arg(ap, char *);
c->cmds[n].s0 = nvstrdup(s);
s = va_arg(ap, char *);
c->cmds[n].s1 = nvstrdup(s);
break;
case DELETE_CMD:
s = va_arg(ap, char *);
c->cmds[n].s0 = nvstrdup(s);
break;
default:
break;
}
va_end(ap);
c->num++;
} /* add_command() */
/*
* add_file_to_list() - concatenate the given directory and filename,
* appending to the FileList structure. If the 'directory' parameter
* is NULL, then just append the filename to the FileList.
*/
static void add_file_to_list(const char *directory,
const char *filename, FileList *l)
{
int len, n = l->num;
l->filename = (char **) nvrealloc(l->filename, sizeof(char *) * (n + 1));
if (directory) {
len = strlen(filename) + strlen(directory) + 2;
l->filename[n] = (char *) nvalloc(len);
snprintf(l->filename[n], len, "%s/%s", directory, filename);
} else {
l->filename[n] = nvstrdup(filename);
}
l->num++;
} /* add_file_to_list() */
static void append_to_rpm_file_list(Options *op, Command *c)
{
FILE *file;
if (!op->rpm_file_list) return;
file = fopen(op->rpm_file_list, "a");
fprintf(file, "%%attr (%04o, root, root) %s\n", c->mode, c->s1);
fclose(file);
}