pkgtools

morpheus pkg tools
git clone git://git.2f30.org/pkgtools.git
Log | Files | Refs | README | LICENSE

commit 4399a3ba705f912991571fb02eee94553fe523c9
Author: sin <sin@2f30.org>
Date:   Thu May 29 14:18:50 +0100

Initial commit

Diffstat:
LICENSE | 21+++++++++++++++++++++
Makefile | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
arg.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
config.mk | 9+++++++++
installpkg.c | 293+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
removepkg.c | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
searchpkg | 26++++++++++++++++++++++++++
strlcat.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
strlcpy.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
util.h | 12++++++++++++
10 files changed, 776 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE @@ -0,0 +1,21 @@ +MIT/X Consortium License + +© 2014 sin <sin@2f30.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,53 @@ +include config.mk + +.POSIX: +.SUFFIXES: .c .o + +LIB = \ + strlcat.o \ + strlcpy.o + +SRC = \ + installpkg.c \ + removepkg.c + +SHPROG = \ + searchpkg + +OBJ = $(SRC:.c=.o) $(LIB) +BIN = $(SRC:.c=) + +all: binlib + +binlib: util.a + $(MAKE) bin + +bin: $(BIN) + +$(OBJ): util.h config.mk + +.o: + @echo LD $@ + @$(LD) -o $@ $< util.a $(LDFLAGS) + +.c.o: + @echo CC $< + @$(CC) -c -o $@ $< $(CFLAGS) + +util.a: $(LIB) + @echo AR $@ + @$(AR) -r -c $@ $(LIB) + +install: all + @echo installing executables to $(DESTDIR)$(PREFIX)/bin + @mkdir -p $(DESTDIR)$(PREFIX)/bin + @cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin + @cp -f $(SHPROG) $(DESTDIR)$(PREFIX)/bin + +uninstall: + @echo removing executables from $(DESTDIR)$(PREFIX)/bin + @cd $(DESTDIR)$(PREFIX)/bin && rm -f $(BIN) $(SHPROG) + +clean: + @echo cleaning + @rm -f $(BIN) $(OBJ) $(LIB) util.a diff --git a/arg.h b/arg.h @@ -0,0 +1,63 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF(base) (brk_ = 1, estrtol(argv[0], (base))) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/config.mk b/config.mk @@ -0,0 +1,9 @@ +VERSION = 0.1 + +PREFIX = /usr/local + +CC = gcc +LD = $(CC) +CPPFLAGS = -D_BSD_SOURCE -D_GNU_SOURCE +CFLAGS = -std=c99 -Wall -Wextra -pedantic $(CPPFLAGS) +LDFLAGS = -s -larchive diff --git a/installpkg.c b/installpkg.c @@ -0,0 +1,293 @@ +/* See LICENSE file for copyright and license details. */ +#include <archive.h> +#include <archive_entry.h> +#include <errno.h> +#include <libgen.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "util.h" + +static void checkdb(const char *); +static void updatedb(const char *, const char *); +static int collisions(const char *, const char *); +static int extract(const char *, const char *); + +char *argv0; + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-f] [-p prefix] pkg...\n", argv0); + fprintf(stderr, " -f Override filesystem checks and force installation\n"); + fprintf(stderr, " -p Set the installation prefix\n"); + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + int i; + char *prefix = "/"; + int fflag = 0; + + ARGBEGIN { + case 'f': + fflag = 1; + break; + case 'p': + prefix = ARGF(); + break; + default: + usage(); + } ARGEND; + + if (argc < 1) + usage(); + + checkdb(prefix); + for (i = 0; i < argc; i++) { + if (fflag == 0) { + if (collisions(prefix, argv[i]) != 0) { + fprintf(stderr, "aborting, use -f to override\n"); + exit(EXIT_FAILURE); + } + } + updatedb(prefix, argv[i]); + extract(prefix, argv[i]); + printf("installed %s\n", argv[i]); + } + return EXIT_SUCCESS; +} + +static void +checkdb(const char *prefix) +{ + char cwd[PATH_MAX]; + char *dbpath = "var/pkg"; + int r; + + getcwd(cwd, sizeof(cwd)); + + r = chdir(prefix); + if (r < 0) { + fprintf(stderr, "chdir %s: %s\n", prefix, + strerror(errno)); + exit(EXIT_FAILURE); + } + + r = access(dbpath, R_OK | W_OK | X_OK); + if (r < 0) { + fprintf(stderr, "access %s: %s\n", dbpath, strerror(errno)); + exit(EXIT_FAILURE); + } + + chdir(cwd); +} + +static void +updatedb(const char *prefix, const char *f) +{ + char cwd[PATH_MAX]; + char path[PATH_MAX], filename[PATH_MAX]; + FILE *fp; + struct archive *in; + struct archive_entry *entry; + int r; + + getcwd(cwd, sizeof(cwd)); + + in = archive_read_new(); + + archive_read_support_filter_gzip(in); + archive_read_support_format_tar(in); + + r = archive_read_open_filename(in, f, BUFSIZ); + if (r < 0) { + fprintf(stderr, "%s\n", archive_error_string(in)); + exit(EXIT_FAILURE); + } + + r = chdir(prefix); + if (r < 0) { + fprintf(stderr, "chdir %s: %s\n", prefix, + strerror(errno)); + exit(EXIT_FAILURE); + } + + strlcpy(path, "var/pkg/", sizeof(path)); + strlcpy(filename, f, sizeof(filename)); + strlcat(path, basename(filename), sizeof(path)); + + fp = fopen(path, "w"); + if (!fp) { + fprintf(stderr, "fopen %s: %s\n", path, + strerror(errno)); + exit(EXIT_FAILURE); + } + + while (1) { + r = archive_read_next_header(in, &entry); + if (r == ARCHIVE_EOF) + break; + if (r != ARCHIVE_OK) { + fprintf(stderr, "%s\n", archive_error_string(in)); + exit(EXIT_FAILURE); + } + fputs(archive_entry_pathname(entry), fp); + fputc('\n', fp); + } + + fflush(fp); + r = fsync(fileno(fp)); + if (r < 0) + fprintf(stderr, "fsync: %s\n", strerror(errno)); + fclose(fp); + + archive_read_close(in); + archive_read_free(in); + + chdir(cwd); +} + +static int +collisions(const char *prefix, const char *f) +{ + char cwd[PATH_MAX]; + struct archive *in; + struct archive_entry *entry; + struct stat sb; + int ok = 0; + int r; + + getcwd(cwd, sizeof(cwd)); + + in = archive_read_new(); + + archive_read_support_filter_gzip(in); + archive_read_support_format_tar(in); + + r = archive_read_open_filename(in, f, BUFSIZ); + if (r < 0) { + fprintf(stderr, "%s\n", archive_error_string(in)); + exit(EXIT_FAILURE); + } + + r = chdir(prefix); + if (r < 0) { + fprintf(stderr, "chdir %s: %s\n", prefix, + strerror(errno)); + exit(EXIT_FAILURE); + } + + while (1) { + r = archive_read_next_header(in, &entry); + if (r == ARCHIVE_EOF) + break; + if (r != ARCHIVE_OK) { + fprintf(stderr, "%s\n", archive_error_string(in)); + exit(EXIT_FAILURE); + } + if (access(archive_entry_pathname(entry), F_OK) == 0) { + r = stat(archive_entry_pathname(entry), &sb); + if (r < 0) { + fprintf(stderr, "stat %s: %s\n", + archive_entry_pathname(entry), + strerror(errno)); + exit(EXIT_FAILURE); + } + if (S_ISDIR(sb.st_mode) == 0) { + fprintf(stderr, "%s/%s exists\n", prefix, + archive_entry_pathname(entry)); + ok = 1; + } + } + } + + archive_read_close(in); + archive_read_free(in); + + chdir(cwd); + return ok; +} + +static int +extract(const char *prefix, const char *f) +{ + char cwd[PATH_MAX]; + const void *buf; + size_t size; + int64_t offset; + struct archive *in; + struct archive *out; + struct archive_entry *entry; + int r; + + getcwd(cwd, sizeof(cwd)); + + in = archive_read_new(); + out = archive_write_disk_new(); + + archive_write_disk_set_options(out, + ARCHIVE_EXTRACT_TIME | + ARCHIVE_EXTRACT_PERM | + ARCHIVE_EXTRACT_ACL | + ARCHIVE_EXTRACT_FFLAGS | + ARCHIVE_EXTRACT_SECURE_NODOTDOT); + + archive_read_support_filter_gzip(in); + archive_read_support_format_tar(in); + + r = archive_read_open_filename(in, f, BUFSIZ); + if (r < 0) { + fprintf(stderr, "%s\n", archive_error_string(in)); + exit(EXIT_FAILURE); + } + + r = chdir(prefix); + if (r < 0) { + fprintf(stderr, "chdir %s: %s\n", prefix, + strerror(errno)); + exit(EXIT_FAILURE); + } + + while (1) { + r = archive_read_next_header(in, &entry); + if (r == ARCHIVE_EOF) + break; + if (r != ARCHIVE_OK) { + fprintf(stderr, "%s\n", archive_error_string(in)); + exit(EXIT_FAILURE); + } + r = archive_write_header(out, entry); + if (r != ARCHIVE_OK) { + fprintf(stderr, "%s\n", archive_error_string(in)); + } else { + while (1) { + r = archive_read_data_block(in, &buf, + &size, &offset); + if (r == ARCHIVE_EOF) + break; + if (r != ARCHIVE_OK) + break; + r = archive_write_data_block(out, buf, size, + offset); + if (r != ARCHIVE_OK) + break; + } + } + } + + archive_read_close(in); + archive_read_free(in); + archive_write_close(out); + archive_write_free(out); + + chdir(cwd); + return 0; +} diff --git a/removepkg.c b/removepkg.c @@ -0,0 +1,201 @@ +/* See LICENSE file for copyright and license details. */ +#define _XOPEN_SOURCE 500 +#include <dirent.h> +#include <errno.h> +#include <ftw.h> +#include <libgen.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "util.h" + +static int rmemptydir(const char *, const struct stat *, int, struct FTW *); +static int removepkg(const char *); + +char *argv0; +static int fflag; + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-f] [-p prefix] pkg...\n", argv0); + fprintf(stderr, " -f Force the removal of empty directories and symlinks\n"); + fprintf(stderr, " -p Set the installation prefix\n"); + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + DIR *dir; + struct dirent *dp; + char filename[PATH_MAX]; + char *prefix = "/"; + int found = 0; + int r; + int i; + + ARGBEGIN { + case 'f': + fflag = 1; + break; + case 'p': + prefix = ARGF(); + break; + default: + usage(); + } ARGEND; + + if (argc < 1) + usage(); + + r = chdir(prefix); + if (r < 0) { + fprintf(stderr, "chdir %s: %s\n", prefix, + strerror(errno)); + return EXIT_FAILURE; + } + + for (i = 0; i < argc; i++) { + strlcpy(filename, argv[i], sizeof(filename)); + found = 0; + + dir = opendir("var/pkg"); + if (!dir) { + fprintf(stderr, "opendir %s: %s\n", "var/pkg", + strerror(errno)); + return EXIT_FAILURE; + } + + while ((dp = readdir(dir))) { + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) + continue; + if (strcmp(dp->d_name, basename(filename)) == 0) { + if (removepkg(argv[i]) != 0) + return EXIT_FAILURE; + found = 1; + break; + } + } + + closedir(dir); + if (found == 0) { + fprintf(stderr, "package %s not installed\n", argv[i]); + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + +static int +rmemptydir(const char *f, const struct stat *sb, int typeflag, + struct FTW *ftwbuf) +{ + (void) sb; + (void) ftwbuf; + + if (typeflag == FTW_DP) + rmdir(f); + return 0; +} + +static int +removepkg(const char *f) +{ + FILE *fp; + struct stat sb; + char buf[BUFSIZ], *p; + char path[PATH_MAX], filename[PATH_MAX]; + int r; + + strlcpy(path, "var/pkg/", sizeof(path)); + strlcpy(filename, f, sizeof(filename)); + strlcat(path, basename(filename), sizeof(path)); + + fp = fopen(path, "r"); + if (!fp) { + fprintf(stderr, "fopen %s: %s\n", path, + strerror(errno)); + return 1; + } + + while (fgets(buf, sizeof(buf), fp)) { + p = strrchr(buf, '\n'); + if (p) + *p = '\0'; + + if (buf[0] == '\0') { + fprintf(stderr, "nil file entry in %s, skipping\n", + path); + continue; + } + + r = lstat(buf, &sb); + if (r < 0) { + fprintf(stderr, "stat %s: %s\n", + buf, strerror(errno)); + continue; + } + + if (S_ISDIR(sb.st_mode)) { + if (fflag == 0) + printf("ignoring directory %s\n", buf); + continue; + } + + if (S_ISLNK(sb.st_mode)) { + if (fflag == 0) { + printf("ignoring link %s\n", buf); + continue; + } + } + + r = remove(buf); + if (r < 0) { + fprintf(stderr, "remove %s: %s\n", buf, + strerror(errno)); + continue; + } + } + if (ferror(fp)) { + fprintf(stderr, "I/O error while processing %s\n", path); + fclose(fp); + return 1; + } + + if (fflag == 1) { + /* prune empty directories as well */ + rewind(fp); + while (fgets(buf, sizeof(buf), fp)) { + p = strrchr(buf, '\n'); + if (p) + *p = '\0'; + if (buf[0] == '\0') + continue; + nftw(buf, rmemptydir, 1, FTW_DEPTH); + } + if (ferror(fp)) { + fprintf(stderr, "I/O error while processing %s\n", path); + fclose(fp); + return 1; + } + } + + fclose(fp); + + /* nuke db entry for this package */ + r = remove(path); + if (r < 0) { + fprintf(stderr, "remove %s: %s\n", path, + strerror(errno)); + return 1; + } + + sync(); + return 0; +} diff --git a/searchpkg b/searchpkg @@ -0,0 +1,26 @@ +#!/bin/sh +# +# To list all packages in the mirror try searchpkg "\.tgz$" +# To download packages try searchpkg pkg... | xargs wget + +release=0.0 +arch=x86_64 +mirror="http://morpheus.2f30.org/$release/packages/$arch" + +usage() { + echo "usage: $(basename $0) pkg..." 1>&2 + exit 1 +} + +if test -z "$1"; then + usage +fi + +curl -s "$mirror/PACKAGES" | while read pkg; do + for i in "$@"; do + echo "$pkg" | grep -q "$i" + if test "$?" -eq 0; then + echo "$mirror/$pkg" | sed 's/#/%23/' + fi + done +done | sort | uniq diff --git a/strlcat.c b/strlcat.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <string.h> +#include "util.h" + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/strlcpy.c b/strlcpy.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <string.h> +#include "util.h" + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + return(s - src - 1); /* count does not include NUL */ +} diff --git a/util.h b/util.h @@ -0,0 +1,12 @@ +/* See LICENSE file for copyright and license details. */ +#include <stddef.h> +#include "arg.h" + +#define LEN(x) (sizeof (x) / sizeof *(x)) + +extern char *argv0; + +#undef strlcat +size_t strlcat(char *, const char *, size_t); +#undef strlcpy +size_t strlcpy(char *, const char *, size_t);