mirror of
https://github.com/pygos/pkg-utils.git
synced 2024-11-22 12:59:46 +01:00
Add install command
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
This commit is contained in:
parent
3298ca1a73
commit
26a7d3f3e7
8 changed files with 403 additions and 2 deletions
|
@ -33,6 +33,11 @@ pkg_SOURCES += main/cmd/pack/desc.c main/cmd/pack/write_hdr.c
|
||||||
pkg_SOURCES += main/cmd/dump/dump.c main/cmd/dump/dump.h main/cmd/dump/dump_toc.c
|
pkg_SOURCES += main/cmd/dump/dump.c main/cmd/dump/dump.h main/cmd/dump/dump_toc.c
|
||||||
pkg_SOURCES += main/cmd/dump/dump_header.c
|
pkg_SOURCES += main/cmd/dump/dump_header.c
|
||||||
|
|
||||||
|
# install command
|
||||||
|
pkg_SOURCES += main/cmd/install/collect.c main/cmd/install/install.c
|
||||||
|
pkg_SOURCES += main/cmd/install/install.h main/cmd/install/pkglist.c
|
||||||
|
pkg_SOURCES += main/cmd/install/tsort.c
|
||||||
|
|
||||||
# unpack command
|
# unpack command
|
||||||
pkg_SOURCES += main/cmd/unpack.c
|
pkg_SOURCES += main/cmd/unpack.c
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ typedef struct pkg_reader_t pkg_reader_t;
|
||||||
|
|
||||||
pkg_reader_t *pkg_reader_open(const char *path);
|
pkg_reader_t *pkg_reader_open(const char *path);
|
||||||
|
|
||||||
|
pkg_reader_t *pkg_reader_open_repo(int dirfd, const char *name);
|
||||||
|
|
||||||
void pkg_reader_close(pkg_reader_t *reader);
|
void pkg_reader_close(pkg_reader_t *reader);
|
||||||
|
|
||||||
int pkg_reader_get_next_record(pkg_reader_t *reader);
|
int pkg_reader_get_next_record(pkg_reader_t *reader);
|
||||||
|
|
67
main/cmd/install/collect.c
Normal file
67
main/cmd/install/collect.c
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#include "install.h"
|
||||||
|
|
||||||
|
int collect_dependencies(int repofd, struct pkg_dep_list *list)
|
||||||
|
{
|
||||||
|
struct pkg_dep_node *it;
|
||||||
|
pkg_dependency_t dep;
|
||||||
|
uint8_t buffer[257];
|
||||||
|
pkg_reader_t *rd;
|
||||||
|
pkg_header_t hdr;
|
||||||
|
size_t i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (it = list->head; it != NULL; it = it->next) {
|
||||||
|
rd = pkg_reader_open_repo(repofd, it->name);
|
||||||
|
if (rd == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = pkg_reader_read_payload(rd, &hdr, sizeof(hdr));
|
||||||
|
if (ret < 0)
|
||||||
|
goto fail;
|
||||||
|
if ((size_t)ret < sizeof(hdr))
|
||||||
|
goto fail_trunc;
|
||||||
|
|
||||||
|
it->num_deps = le16toh(hdr.num_depends);
|
||||||
|
it->deps = calloc(sizeof(it->deps[0]), it->num_deps);
|
||||||
|
if (it->deps == NULL)
|
||||||
|
goto fail_oom;
|
||||||
|
|
||||||
|
for (i = 0; i < it->num_deps; ++i) {
|
||||||
|
ret = pkg_reader_read_payload(rd, &dep, sizeof(dep));
|
||||||
|
if (ret < 0)
|
||||||
|
goto fail;
|
||||||
|
if ((size_t)ret < sizeof(hdr))
|
||||||
|
goto fail_trunc;
|
||||||
|
|
||||||
|
ret = pkg_reader_read_payload(rd, buffer,
|
||||||
|
dep.name_length);
|
||||||
|
if (ret < 0)
|
||||||
|
goto fail;
|
||||||
|
if ((size_t)ret < dep.name_length)
|
||||||
|
goto fail_trunc;
|
||||||
|
|
||||||
|
buffer[dep.name_length] = '\0';
|
||||||
|
|
||||||
|
it->deps[i] = find_pkg(list, (char *)buffer);
|
||||||
|
if (it->deps[i] == NULL) {
|
||||||
|
it->deps[i] = append_pkg(list, (char *)buffer);
|
||||||
|
if (it->deps[i] == NULL)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg_reader_close(rd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fail_trunc:
|
||||||
|
fprintf(stderr, "%s: truncated header record\n",
|
||||||
|
pkg_reader_get_filename(rd));
|
||||||
|
goto fail;
|
||||||
|
fail_oom:
|
||||||
|
fputs("out of memory\n", stderr);
|
||||||
|
goto fail;
|
||||||
|
fail:
|
||||||
|
pkg_reader_close(rd);
|
||||||
|
return -1;
|
||||||
|
}
|
137
main/cmd/install/install.c
Normal file
137
main/cmd/install/install.c
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#include "install.h"
|
||||||
|
|
||||||
|
static const struct option long_opts[] = {
|
||||||
|
{ "root", required_argument, NULL, 'r' },
|
||||||
|
{ "no-chown", required_argument, NULL, 'o' },
|
||||||
|
{ "no-chmod", required_argument, NULL, 'm' },
|
||||||
|
{ "repo-dir", required_argument, NULL, 'R' },
|
||||||
|
{ NULL, 0, NULL, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *short_opts = "R:r:om";
|
||||||
|
|
||||||
|
static int unpack_packages(int repofd, int rootfd, int flags,
|
||||||
|
struct pkg_dep_list *list)
|
||||||
|
{
|
||||||
|
struct pkg_dep_node *it;
|
||||||
|
pkg_reader_t *rd;
|
||||||
|
|
||||||
|
for (it = list->head; it != NULL; it = it->next) {
|
||||||
|
rd = pkg_reader_open_repo(repofd, it->name);
|
||||||
|
if (rd == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (pkg_unpack(rootfd, flags, rd)) {
|
||||||
|
pkg_reader_close(rd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg_reader_close(rd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_install(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i, rootfd = AT_FDCWD, repofd = AT_FDCWD, flags = 0;
|
||||||
|
struct pkg_dep_list list;
|
||||||
|
int ret = EXIT_FAILURE;
|
||||||
|
|
||||||
|
memset(&list, 0, sizeof(list));
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
|
||||||
|
if (i == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 'R':
|
||||||
|
if (repofd != AT_FDCWD) {
|
||||||
|
fputs("repo specified more than once\n",
|
||||||
|
stderr);
|
||||||
|
tell_read_help(argv[0]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
repofd = open(optarg, O_RDONLY | O_DIRECTORY);
|
||||||
|
if (repofd < 0) {
|
||||||
|
perror(optarg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (rootfd != AT_FDCWD) {
|
||||||
|
fputs("root specified more than once\n",
|
||||||
|
stderr);
|
||||||
|
tell_read_help(argv[0]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mkdir_p(optarg))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
rootfd = open(optarg, O_RDONLY | O_DIRECTORY);
|
||||||
|
if (rootfd < 0) {
|
||||||
|
perror(optarg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
flags |= UNPACK_NO_CHOWN;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
flags |= UNPACK_NO_CHMOD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tell_read_help(argv[0]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = optind; i < argc; ++i) {
|
||||||
|
if (append_pkg(&list, argv[i]) == NULL)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collect_dependencies(repofd, &list))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (sort_by_dependencies(&list))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (unpack_packages(repofd, rootfd, flags, &list))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = EXIT_SUCCESS;
|
||||||
|
out:
|
||||||
|
if (rootfd != AT_FDCWD)
|
||||||
|
close(rootfd);
|
||||||
|
if (repofd != AT_FDCWD)
|
||||||
|
close(repofd);
|
||||||
|
pkg_list_cleanup(&list);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static command_t install = {
|
||||||
|
.cmd = "install",
|
||||||
|
.usage = "[OPTIONS...] PACKAGES...",
|
||||||
|
.s_desc = "install packages and their dependencies",
|
||||||
|
.l_desc =
|
||||||
|
"The install command fetches a number of packages from a repository directory\n"
|
||||||
|
"by name and extracts them to a specified root directory. Dependencies of the\n"
|
||||||
|
"packages are evaluated and also installed.\n"
|
||||||
|
"\n"
|
||||||
|
"Possible options:\n"
|
||||||
|
" --repo-dir, -R <path> Specify the input repository path to fetch the\n"
|
||||||
|
" packages from.\n"
|
||||||
|
" --root, -r <path> A root directory to unpack the package. Defaults\n"
|
||||||
|
" to the current working directory if not set.\n"
|
||||||
|
" --no-chown, -o Do not change ownership of the extracted data.\n"
|
||||||
|
" Keep the uid/gid of the user who runs the program.\n"
|
||||||
|
" --no-chmod, -m Do not change permission flags of the extarcted\n"
|
||||||
|
" data. Use 0644 for all files and 0755 for all\n"
|
||||||
|
" directories.\n",
|
||||||
|
.run_cmd = cmd_install,
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_COMMAND(install)
|
44
main/cmd/install/install.h
Normal file
44
main/cmd/install/install.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef INSTALL_H
|
||||||
|
#define INSTALL_H
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "pkgreader.h"
|
||||||
|
#include "command.h"
|
||||||
|
#include "pkgio.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
struct pkg_dep_node {
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
/* num dependencies == number of outgoing edges */
|
||||||
|
size_t num_deps;
|
||||||
|
|
||||||
|
/* direct dependencies == array of outgoing edges */
|
||||||
|
struct pkg_dep_node **deps;
|
||||||
|
|
||||||
|
/* linked list pointer */
|
||||||
|
struct pkg_dep_node *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pkg_dep_list {
|
||||||
|
struct pkg_dep_node *head;
|
||||||
|
struct pkg_dep_node *tail;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pkg_dep_node *append_pkg(struct pkg_dep_list *list, const char *name);
|
||||||
|
|
||||||
|
struct pkg_dep_node *find_pkg(struct pkg_dep_list *list, const char *name);
|
||||||
|
|
||||||
|
void pkg_list_cleanup(struct pkg_dep_list *list);
|
||||||
|
|
||||||
|
int collect_dependencies(int repofd, struct pkg_dep_list *list);
|
||||||
|
|
||||||
|
int sort_by_dependencies(struct pkg_dep_list *list);
|
||||||
|
|
||||||
|
#endif /* INSTALL_H */
|
51
main/cmd/install/pkglist.c
Normal file
51
main/cmd/install/pkglist.c
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#include "install.h"
|
||||||
|
|
||||||
|
struct pkg_dep_node *append_pkg(struct pkg_dep_list *list, const char *name)
|
||||||
|
{
|
||||||
|
struct pkg_dep_node *new = calloc(1, sizeof(*new));
|
||||||
|
|
||||||
|
if (new == NULL)
|
||||||
|
goto fail_oom;
|
||||||
|
|
||||||
|
new->name = strdup(name);
|
||||||
|
if (new->name == NULL)
|
||||||
|
goto fail_oom;
|
||||||
|
|
||||||
|
if (list->tail == NULL) {
|
||||||
|
list->head = list->tail = new;
|
||||||
|
} else {
|
||||||
|
list->tail->next = new;
|
||||||
|
list->tail = new;
|
||||||
|
}
|
||||||
|
return new;
|
||||||
|
fail_oom:
|
||||||
|
fputs("out of memory\n", stderr);
|
||||||
|
free(new);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pkg_dep_node *find_pkg(struct pkg_dep_list *list, const char *name)
|
||||||
|
{
|
||||||
|
struct pkg_dep_node *it = list->head;
|
||||||
|
|
||||||
|
while (it != NULL && strcmp(it->name, name) != 0)
|
||||||
|
it = it->next;
|
||||||
|
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pkg_list_cleanup(struct pkg_dep_list *list)
|
||||||
|
{
|
||||||
|
struct pkg_dep_node *node;
|
||||||
|
|
||||||
|
while (list->head != NULL) {
|
||||||
|
node = list->head;
|
||||||
|
list->head = node->next;
|
||||||
|
|
||||||
|
free(node->deps);
|
||||||
|
free(node->name);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
list->tail = NULL;
|
||||||
|
}
|
69
main/cmd/install/tsort.c
Normal file
69
main/cmd/install/tsort.c
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#include "install.h"
|
||||||
|
|
||||||
|
int sort_by_dependencies(struct pkg_dep_list *list)
|
||||||
|
{
|
||||||
|
struct pkg_dep_node *it, *prev, *pkg;
|
||||||
|
struct pkg_dep_list result;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
result.head = NULL;
|
||||||
|
result.tail = NULL;
|
||||||
|
|
||||||
|
while (list->head != NULL) {
|
||||||
|
/* find node with no outgoing edges */
|
||||||
|
prev = NULL;
|
||||||
|
it = list->head;
|
||||||
|
|
||||||
|
while (it != NULL && it->num_deps != 0) {
|
||||||
|
prev = it;
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it == NULL) {
|
||||||
|
fputs("cycle detected in dependency graph\n", stderr);
|
||||||
|
pkg_list_cleanup(&result);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove from graph */
|
||||||
|
if (prev == NULL) {
|
||||||
|
list->head = it->next;
|
||||||
|
} else {
|
||||||
|
prev->next = it->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it == list->tail)
|
||||||
|
list->tail = prev;
|
||||||
|
|
||||||
|
/* remove edges pointing to the package */
|
||||||
|
pkg = it;
|
||||||
|
|
||||||
|
for (it = list->head; it != NULL; it = it->next) {
|
||||||
|
for (i = 0; i < it->num_deps; ++i) {
|
||||||
|
if (it->deps[i] == pkg) {
|
||||||
|
it->deps[i] =
|
||||||
|
it->deps[it->num_deps - 1];
|
||||||
|
it->num_deps -= 1;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->num_deps == 0 && it->deps != NULL) {
|
||||||
|
free(it->deps);
|
||||||
|
it->deps = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append to list */
|
||||||
|
if (result.tail == NULL) {
|
||||||
|
result.head = result.tail = pkg;
|
||||||
|
} else {
|
||||||
|
result.tail->next = pkg;
|
||||||
|
result.tail = pkg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list->head = result.head;
|
||||||
|
list->tail = result.tail;
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "pkgreader.h"
|
#include "pkgreader.h"
|
||||||
|
@ -118,7 +119,7 @@ fail_comp:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg_reader_t *pkg_reader_open(const char *path)
|
static pkg_reader_t *pkg_reader_openat(int dirfd, const char *path)
|
||||||
{
|
{
|
||||||
pkg_reader_t *rd = calloc(1, sizeof(*rd));
|
pkg_reader_t *rd = calloc(1, sizeof(*rd));
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -129,7 +130,7 @@ pkg_reader_t *pkg_reader_open(const char *path)
|
||||||
}
|
}
|
||||||
|
|
||||||
rd->path = path;
|
rd->path = path;
|
||||||
rd->fd = open(path, O_RDONLY);
|
rd->fd = openat(dirfd, path, O_RDONLY);
|
||||||
if (rd->fd < 0) {
|
if (rd->fd < 0) {
|
||||||
perror(path);
|
perror(path);
|
||||||
free(rd);
|
free(rd);
|
||||||
|
@ -150,6 +151,31 @@ fail:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pkg_reader_t *pkg_reader_open(const char *path)
|
||||||
|
{
|
||||||
|
return pkg_reader_openat(AT_FDCWD, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg_reader_t *pkg_reader_open_repo(int dirfd, const char *name)
|
||||||
|
{
|
||||||
|
char *fname;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; name[i] != '\0'; ++i) {
|
||||||
|
if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-') {
|
||||||
|
fprintf(stderr,
|
||||||
|
"illegal characters in package name '%s'\n",
|
||||||
|
name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fname = alloca(strlen(name) + 5);
|
||||||
|
sprintf(fname, "%s.pkg", name);
|
||||||
|
|
||||||
|
return pkg_reader_openat(dirfd, fname);
|
||||||
|
}
|
||||||
|
|
||||||
void pkg_reader_close(pkg_reader_t *rd)
|
void pkg_reader_close(pkg_reader_t *rd)
|
||||||
{
|
{
|
||||||
if (rd->stream != NULL)
|
if (rd->stream != NULL)
|
||||||
|
|
Loading…
Reference in a new issue