├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README ├── include ├── action.h ├── arr.h ├── buf.h ├── cache.h ├── download.h ├── error.h ├── file.h ├── pkg.h ├── repo.h ├── sha256.h ├── tar.h ├── test.h └── util.h ├── make ├── src ├── action.c ├── action │ ├── alt.c │ ├── build.c │ ├── checksum.c │ ├── download.c │ ├── list.c │ └── search.c ├── arr.c ├── buf.c ├── cache.c ├── download.c ├── file.c ├── kiss.c ├── pkg.c ├── repo.c ├── sha256.c ├── tar.c ├── test.c └── util.c └── test ├── test-action.c ├── test-arr.c ├── test-buf.c ├── test-cache.c ├── test-download.c ├── test-sha256.c ├── test-tar.c ├── test-util.c ├── test_hier ├── bin │ └── kiss-test ├── repo │ ├── core │ │ ├── gzip │ │ │ ├── build │ │ │ ├── checksums │ │ │ ├── sources │ │ │ └── version │ │ └── zlib │ │ │ ├── build │ │ │ ├── checksums │ │ │ ├── sources │ │ │ └── version │ └── extra │ │ └── samurai │ │ ├── build │ │ ├── checksums │ │ ├── sources │ │ └── version └── var │ └── db │ └── kiss │ └── installed │ ├── gzip │ ├── samurai │ └── zlib └── valgrind ├── lzma.supp └── musl.supp /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | - name: Run CI. 9 | run: | 10 | sudo apt install tcc valgrind libcurl4-openssl-dev libarchive-dev 11 | export OPENSSL=0 12 | export LIBARCHIVE=1 13 | export CFLAGS='-Werror -O3' 14 | CC=clang time ./make 15 | CC=tcc time ./make 16 | CC=gcc time ./make 17 | export CFLAGS='-Werror -O0 -g -fprofile-arcs -ftest-coverage' 18 | export LDFLAGS='-lgcov --coverage' 19 | CC=gcc ./make 20 | ./make check 21 | 22 | - uses: codecov/codecov-action@v1 23 | with: 24 | fail_ci_if_error: true 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | kiss 2 | */*.o 3 | */*/*.o 4 | .ccls-cache 5 | compile_commands.json 6 | .ninja* 7 | .cache 8 | *.s 9 | */*.s 10 | test/test-*[!.]* 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Dylan Araps 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | |/ 2 | |\ISS https://k1ss.org 3 | ________________________________________________________________________________ 4 | 5 | 6 | Next KISS Package Manager 7 | ________________________________________________________________________________ 8 | 9 | The at-some-point-in-the-future replacement for the current POSIX shell 10 | package manager written in C99. 11 | 12 | 13 | 14 | TODO 15 | ________________________________________________________________________________ 16 | 17 | - [ ] actions 18 | - [0] alternatives 19 | - [x] list all 20 | - [ ] swap alts 21 | - [0] build 22 | - [x] checksum 23 | - [x] find self-contained / permissively licensed sha256 implementation 24 | - [x] parse sources file 25 | - [x] generate checksums file 26 | - [x] download 27 | - [x] libcurl stuff 28 | - [x] catch Ctrl+C during download 29 | - [ ] install 30 | - [x] list 31 | - [x] Individual 32 | - [x] All 33 | - [ ] remove 34 | - [x] search 35 | - [ ] update 36 | - [ ] avoid libgit2 (requires cmake, no support for shallow clones, etc) 37 | - [ ] possibly fork/exec command-line git initially 38 | - [ ] help-ext 39 | - [ ] environment variables 40 | - [0] KISS_ROOT 41 | - [ ] KISS_PROMPT 42 | - [X] KISS_PATH 43 | - [ ] KISS_STRIP 44 | - [ ] KISS_COMPRESS 45 | - [ ] KISS_FORCE 46 | - [ ] KISS_CHOICE 47 | - [ ] KISS_DEBUG 48 | - Will be changed to print extra debugging information. 49 | - [X] KISS_TMPDIR 50 | - [ ] components 51 | - [0] source extraction / tarball creation 52 | - [0] avoid libarchive 53 | - [x] tar utility fallback 54 | - [ ] alternative tar library 55 | - [x] possibly fork/exec command-line tar initially 56 | - [x] support zip 57 | - [ ] checksum verification 58 | - [ ] add support for SKIP 59 | - [ ] add checksum verification 60 | - [x] repositories 61 | - [x] caches 62 | - [ ] dependency resolution 63 | - [ ] lets not make it recursive this time 64 | - [ ] dependency fixer 65 | - [ ] elf parser 66 | - [ ] binary stripping 67 | - [ ] elf parser 68 | - [ ] package conflict detection 69 | - [ ] repository hooks 70 | - [ ] manifest generation 71 | - [ ] requires recursive file tree walk 72 | - [ ] avoid nftw() (XSI, not POSIX) 73 | - [ ] 3-way handshake for files in /etc/ 74 | - [ ] misc 75 | - [x] crux-like usage 76 | - [ ] logging 77 | - [x] pretty output 78 | - [x] string library 79 | - [x] str_push_int() (or snprintf wrapper) 80 | - [x] list library 81 | - [ ] privileges 82 | - Run as root + drop permissions? (shell method not feasible) 83 | -------------------------------------------------------------------------------- /include/action.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_ACTION_H 6 | #define KISS_ACTION_H 7 | 8 | #include "buf.h" 9 | #include "cache.h" 10 | #include "pkg.h" 11 | #include "repo.h" 12 | 13 | struct state { 14 | int opt; 15 | struct cache cache; 16 | struct repo **repos; 17 | pkg **pkgs; 18 | char **argv; 19 | buf *mem; 20 | }; 21 | 22 | enum state_options { 23 | STATE_CACHE = (1L << 0), 24 | STATE_REPO = (1L << 1), 25 | STATE_PKG = (1L << 2), 26 | STATE_PKG_REPO = (1L << 3), 27 | STATE_PKG_CACHE = (1L << 4), 28 | STATE_PKG_PWD = (1L << 5), 29 | STATE_MEM = (1L << 6), 30 | STATE_ARGV = (1L << 7), 31 | STATE_KISS_PATH = (1L << 8), 32 | }; 33 | #define STATE_ALL (~0L) 34 | #define STATE_SEARCH (STATE_PKG | STATE_MEM | STATE_REPO | STATE_KISS_PATH) 35 | 36 | /** 37 | * initialize state 38 | */ 39 | struct state *state_init(int argc, char *argv[], int opt); 40 | 41 | /** 42 | * initialize pkg 43 | */ 44 | int state_init_pkg(struct state *s, char *p); 45 | 46 | /** 47 | * free state 48 | */ 49 | void state_free(struct state *s); 50 | 51 | /** 52 | * kiss alt 53 | */ 54 | int action_alt(struct state *s); 55 | 56 | /** 57 | * kiss build 58 | */ 59 | int action_build(struct state *s); 60 | 61 | /** 62 | * kiss checksum 63 | */ 64 | int action_checksum(struct state *s); 65 | 66 | /** 67 | * kiss download 68 | */ 69 | int action_download(struct state *s); 70 | 71 | /** 72 | * kiss list 73 | */ 74 | int action_list(struct state *s); 75 | 76 | /** 77 | * kiss search 78 | */ 79 | int action_search(struct state *s); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /include/arr.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_ARR_H 6 | #define KISS_ARR_H 7 | 8 | #include 9 | #include 10 | 11 | /** 12 | * Allocate or grow array by l bytes. If a is NULL, memory will be allocted 13 | * for the array. If a is not NULL, the array will grow by l elements. Returns 14 | * NULL if memory allocation fails and a is unmodified. 15 | * 16 | * Functions will call this internally for you. This is only really needed 17 | * during array creation. 18 | * 19 | * // create array, don't allocate any extra memory for elements. 20 | * char **new = arr_alloc(0, 0); 21 | * 22 | * // create array and preallocate memory for 20 elements. 23 | * char **new = buf_alloc(0, 20); 24 | * 25 | */ 26 | void *arr_alloc(void *a, size_t l); 27 | 28 | /** 29 | * Free the array (caller must free elements). 30 | */ 31 | #define arr_free(a) free(a ? arr_raw(a) : 0) 32 | 33 | /** 34 | * Sort array using qsort. cb equates to the function pointer 35 | * given to qsort: int (*cb)(const void *, const void *) 36 | */ 37 | #define arr_sort(a, cb) qsort(a, arr_len(a), sizeof(void *), cb) 38 | 39 | /** 40 | * Drop element from back of array. 41 | */ 42 | #define arr_drop_b(a) arr_rem_len(a, 1) 43 | 44 | /** 45 | * Various macros to ease data accesss. 46 | */ 47 | #define arr_raw(a) ((size_t *) (a) - 2) 48 | #define arr_len(a) (arr_raw(a)[1]) 49 | #define arr_cap(a) (arr_raw(a)[0]) 50 | #define arr_set_end(a, d) ((a)[arr_len(a)++] = (d)) 51 | #define arr_add_len(a, l) (arr_len(a) += (l)) 52 | #define arr_rem_len(a, l) (arr_len(a) -= (l)) 53 | #define arr_inc_cap(a) (arr_cap(a) + (arr_cap(a) >> 1)) 54 | 55 | /** 56 | * Grow the array if l additional items do not fit. 57 | */ 58 | #define arr_alloc_maybe(a, l) do { \ 59 | if ((arr_len(a) + l) > arr_cap(a)) { \ 60 | void *_n = arr_alloc(a, arr_inc_cap(a)); \ 61 | assert(_n); \ 62 | (a) = _n; \ 63 | } \ 64 | } while(0) 65 | 66 | /** 67 | * Push an element to the list, growing it by a factor of 1.5 (if needed). 68 | */ 69 | #define arr_push_b(a, d) do { \ 70 | arr_alloc_maybe(a, 1); \ 71 | arr_set_end(a, d); \ 72 | } while(0) 73 | 74 | #endif 75 | 76 | -------------------------------------------------------------------------------- /include/buf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_BUF_H 6 | #define KISS_BUF_H 7 | 8 | #include 9 | #include 10 | 11 | typedef char buf; 12 | 13 | /** 14 | * Allocate or grow a string by l bytes. If s is NULL, memory will be allocted 15 | * for the string. If s is not NULL, the string will grow by l bytes. Returns 16 | * NULL if memory allocation fails and string is unmodified. 17 | * 18 | * Functions will call this internally for you. This is only really needed 19 | * during string creation. 20 | * 21 | * // create string, don't allocate any extra memory for char array. 22 | * buf *new = buf_alloc(0, 0); 23 | * 24 | * // create string and preallocate 20 bytes of memory for char array. 25 | * buf *new = buf_alloc(0, 20); 26 | * 27 | */ 28 | buf *buf_alloc(buf **s, size_t l); 29 | 30 | /** 31 | * Free all memory associated with the string. Checks for NULL before calling 32 | * free(). 33 | */ 34 | void buf_free(buf **s); 35 | 36 | /** 37 | * Push a string of known length. Returns 0 for success and -ENOMEM if memory 38 | * allocation fails. 39 | */ 40 | int buf_push_l(buf **s, const char *d, size_t l); 41 | 42 | /** 43 | * Push a string of unknown length. Returns 0 for success, -EINVAL if d is 44 | * NULL and -ENOMEM if memory allocation fails. Wrapper around buf_push_l() 45 | * using buflen(d) to determine length of input. 46 | */ 47 | int buf_push_s(buf **s, const char *d); 48 | 49 | /** 50 | * Push a character. Returns 0 for success, -ENOMEM if memory allocation fails. 51 | */ 52 | int buf_push_c(buf **s, int d); 53 | 54 | /** 55 | * Drop a character from the end of the string if it matches d. Returns 0 for 56 | * success and -1 for failure. 57 | */ 58 | int buf_undo_c(buf **s, int d); 59 | 60 | /** 61 | * Drop every character matching d from the end of the string. 62 | */ 63 | void buf_rstrip(buf **s, int d); 64 | 65 | /** 66 | * Push the next line in f to the string in chunks of l. No additional memory 67 | * is allocated for each line read (unless the buffer must be grown). The 68 | * trailing newline is removed if present. Returns 0 for success and -1 for 69 | * failure. 70 | */ 71 | int buf_getline(buf **s, FILE *f, size_t l); 72 | 73 | /** 74 | * Wrapper around vsnprintf() which automatically grows buffer if needed. 75 | * Returns 0 on success and -ENOMEM or -1 on failure. 76 | */ 77 | int buf_printf(buf **s, const char *f, ...); 78 | 79 | /** 80 | * Wrapper around memset() which automatically grows buffer if needed. 81 | * Returns 0 on success and -ENOMEM on failure. 82 | */ 83 | int buf_set(buf **s, int c, size_t l); 84 | 85 | /** 86 | * Find first occurance of c from position l, zero this position in 87 | * the string and return pos + 1. Can be used as a quick tokenizer. 88 | * Length is not updated. 89 | */ 90 | size_t buf_scan(buf **s, size_t l, int c); 91 | 92 | /** 93 | * Find last occurance of c, zero this position in the string and 94 | * return pos + 1. Length is not updated. 95 | */ 96 | size_t buf_scan_rev(buf **s, int c); 97 | 98 | /** 99 | * Find first occurance of f and replace it with r starting search from l. 100 | * Returns 1 if find/replace was done, else 0. 101 | */ 102 | int buf_fr_c(buf **s, size_t l, char f, char r); 103 | 104 | /** 105 | * Find all occurances of f and replace them with r starting search from l. 106 | */ 107 | void buf_fr_cg(buf **s, size_t l, char f, char r); 108 | 109 | /** 110 | * Get pointer to beginning of memory allocation. 111 | */ 112 | #define buf_raw(s) ((s) ? (*s) ? ((size_t *) (*s) - 2) : 0 : 0) 113 | 114 | /** 115 | * Get the length of a string. 116 | */ 117 | #define buf_len(s) (((size_t *) (s))[-1]) 118 | 119 | /** 120 | * Get the capacity (memory allocated) of a string. 121 | */ 122 | #define buf_cap(s) (((size_t *) (s))[-2]) 123 | 124 | /** 125 | * Last character in string. 126 | */ 127 | #define buf_end(s) ((s)[buf_len(s) - 1]) 128 | 129 | /** 130 | * Change the string's length to l and set the corresponding position in the 131 | * string to '\0'. This can be used to safely (and cheaply) truncate strings. 132 | */ 133 | #define buf_set_len(s, l) (((s)[buf_len(s) = (l)]) = 0) 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /include/cache.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_CACHE_H 6 | #define KISS_CACHE_H 7 | 8 | #include "buf.h" 9 | 10 | struct cache { 11 | buf *dir; 12 | int fd[7]; 13 | }; 14 | 15 | enum cache_type { 16 | CAC_MAK, 17 | CAC_PKG, 18 | CAC_EXT, 19 | 20 | CAC_SRC, 21 | CAC_LOG, 22 | CAC_BIN, 23 | 24 | CAC_DIR, 25 | }; 26 | 27 | int cache_init(struct cache *c); 28 | int cache_init_all(struct cache *c); 29 | int cache_init_pkg(struct cache *c, const char *pkg); 30 | int cache_get_base(buf **c); 31 | FILE *cache_fopen(int fd, const char *pkg, const char *des, const char *f, 32 | int M, const char *m); 33 | int cache_mkdirat(int fd, const char *pkg, const char *des); 34 | int cache_clean(struct cache *c); 35 | void cache_free(struct cache *c); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /include/download.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_DOWNLOAD_H 6 | #define KISS_DOWNLOAD_H 7 | 8 | #include 9 | 10 | /** 11 | * Download a URL to the given file pointer. This function will initialize curl 12 | * on first use. Subsequent calls reuse the same curl handle. Returns 0 for 13 | * success and -1 for error. 14 | * 15 | * On error, any written data is not removed. This must be done by the caller. 16 | */ 17 | int source_download(const char *url, FILE *dest); 18 | 19 | /** 20 | * Call the global curl clean up functions. This should only be called a single 21 | * time and only once curl is no longer needed. 22 | */ 23 | void source_curl_cleanup(void); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/error.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_ERROR_H 6 | #define KISS_ERROR_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | /** 13 | * Display an error message with file, function and line number infomation. 14 | */ 15 | #define err(...) do { \ 16 | fprintf(stderr, "error: "); \ 17 | fprintf(stderr, __VA_ARGS__); \ 18 | fprintf(stderr, " (%s in %s() at line %d)", \ 19 | __FILE__, __func__, __LINE__); \ 20 | fputc('\n', stderr); \ 21 | } while (0) 22 | 23 | /** 24 | * Display an error message with file, function and line number information. 25 | * Also, append the operating system error via strerror(). 26 | */ 27 | #define err_no(...) do { \ 28 | fprintf(stderr, "error: "); \ 29 | fprintf(stderr, __VA_ARGS__); \ 30 | fprintf(stderr, ": %s", strerror(errno)); \ 31 | fprintf(stderr, " (%s in %s() at line %d)", \ 32 | __FILE__, __func__, __LINE__); \ 33 | fputc('\n', stderr); \ 34 | } while (0) 35 | 36 | /** 37 | * Display a message. 38 | */ 39 | #define msg(...) do { \ 40 | fprintf(stdout, __VA_ARGS__); \ 41 | fputc('\n', stdout); \ 42 | } while (0) 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /include/file.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_FILE_H 6 | #define KISS_FILE_H 7 | 8 | #include 9 | #include 10 | 11 | /** 12 | * Recursively create a directory tree. The last path component is skipped 13 | * unless the path ends in '/'. Returns < 0 for error. 14 | */ 15 | int mkdir_p(const char *path, mode_t m); 16 | 17 | /** 18 | * Recursively remove a directory tree. Returns < 0 for error. 19 | */ 20 | int rm_rf(const char *path); 21 | 22 | /** 23 | * Wrapper around openat() to return a FILE rather than an fd. 24 | */ 25 | FILE *fopenat(int fd, const char *p, int m, const char *M); 26 | 27 | /** 28 | * Wrapper around openat() to return a FILE rather than an fd. 29 | * (opens p at fd and then f at p) 30 | */ 31 | FILE *fopenatat(int fd, const char *p, const char *f, int m, const char *M); 32 | 33 | /** 34 | * Wrapper around readdir() to skip '.' and '..'. 35 | */ 36 | struct dirent *read_dir(DIR *d); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/pkg.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_PKG_H 6 | #define KISS_PKG_H 7 | 8 | typedef struct { 9 | char *name; 10 | int repo_fd; 11 | } pkg; 12 | 13 | pkg *pkg_alloc(const char *name); 14 | int pkg_find_path(pkg *p); 15 | void pkg_free(pkg *p); 16 | void pkg_free_all(pkg **p); 17 | FILE *pkg_fopen(pkg *p, const char *f, int M, const char *m); 18 | int pkg_faccessat(int repo_fd, const char *pkg, const char *file); 19 | int pkg_sort_name(void const *a, void const *b); 20 | 21 | enum pkg_source_types { 22 | SRC_URL, 23 | SRC_GIT, 24 | SRC_ABS, 25 | SRC_REL, 26 | }; 27 | int pkg_source_type(pkg *p, char *src); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/repo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_REPO_H 6 | #define KISS_REPO_H 7 | 8 | struct repo { 9 | int fd; 10 | char path[]; 11 | }; 12 | 13 | /** 14 | * Open a repository. Returns NULL on error. 15 | */ 16 | struct repo *repo_open(const char *path); 17 | 18 | /** 19 | * Open a $PATH and store each path in the array r. Returns < 0 on error; 20 | */ 21 | int repo_open_PATH(struct repo **r, const char *PATH); 22 | 23 | /** 24 | * Open the installed database of type. Returns NULL on error; 25 | */ 26 | struct repo *repo_open_db(const char *type); 27 | 28 | /** 29 | * Open the installed database of type and push it to the array. Returns -1 30 | * on error; 31 | */ 32 | int repo_open_db_push(struct repo **r, const char *type); 33 | 34 | /** 35 | * Check if package exists in repository. Returns 1 for success and 0 for 36 | * error. 37 | */ 38 | int repo_has_pkg(struct repo *r, const char *pkg); 39 | /** 40 | * Find the first occurrence of pkg in a repository list; 41 | */ 42 | int repo_find_pkg(struct repo **r, const char *pkg); 43 | 44 | /** 45 | * Free all memory associated with the repository and close its fd; 46 | */ 47 | void repo_free(struct repo *r); 48 | 49 | /** 50 | * Free all memory and close any fds associated with a list of repositories. 51 | * Also frees the array itself. 52 | */ 53 | void repo_free_all(struct repo **r); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/sha256.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_SHA256_H 6 | #define KISS_SHA256_H 7 | 8 | #include 9 | #include 10 | 11 | #define SHA256_LEN 32 12 | 13 | /** 14 | * Use OpenSSL sha256 if available falling back to a standalone implementation. 15 | * OpenSSL has ASM implementations of sha256 (which use SSSE3, AVX, whatever) 16 | * for various architectures. 17 | */ 18 | #if defined(SHA256_USE_OPENSSL) 19 | #include 20 | 21 | /** 22 | * Use bearssl sha256 if available falling back to a standalone implementation. 23 | * (This is the fastest implementation in my testing). 24 | */ 25 | #elif defined(SHA256_USE_BEARSSL) 26 | #include 27 | 28 | typedef br_sha256_context SHA256_CTX; 29 | 30 | #define SHA256_Init(c) br_sha256_init(c) 31 | #define SHA256_Update(c, d, l) br_sha256_update(c, d, l) 32 | #define SHA256_Final(md, c) br_sha256_out(c, md) 33 | 34 | /** 35 | * Fallback internal sha256 implementation. 36 | */ 37 | #else 38 | 39 | typedef struct SHA256state_st { 40 | uint32_t state[8]; 41 | uint64_t count; 42 | unsigned char buf[SHA256_LEN * 2]; 43 | } SHA256_CTX; 44 | 45 | void SHA256_Init(SHA256_CTX *c); 46 | void SHA256_Update(SHA256_CTX *c, const void *data, size_t len); 47 | void SHA256_Final(unsigned char *md, SHA256_CTX *c); 48 | 49 | #endif 50 | 51 | /** 52 | * Generate checksums for a file. Result is stored in hash. 53 | */ 54 | void sha256_file(unsigned char hash[SHA256_LEN], FILE *f); 55 | 56 | /** 57 | * Convert hash to its string representation. Result is stored in out. 58 | */ 59 | void sha256_to_string(unsigned char hash[SHA256_LEN], char out[65]); 60 | 61 | #endif 62 | 63 | -------------------------------------------------------------------------------- /include/tar.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_TAR_H 6 | #define KISS_TAR_H 7 | 8 | /** 9 | * Use libarchive for tar archives. 10 | */ 11 | #ifdef TAR_USE_LIBARCHIVE 12 | #include 13 | #include 14 | 15 | /** 16 | * Fallback to executing tar utility if libarchive not available. 17 | */ 18 | #else 19 | 20 | 21 | #endif 22 | 23 | enum compression_type { 24 | TAR_NONE, 25 | TAR_BZ2, 26 | TAR_GZ, 27 | TAR_LZ, 28 | TAR_LZMA, 29 | TAR_XZ, 30 | TAR_ZSTD, 31 | }; 32 | 33 | /** 34 | * Create a tar archive from contents of directory d, save to file f. 35 | * compression is one of the above enum values. 36 | */ 37 | int tar_create(const char *d, const char *f, int compression); 38 | 39 | /** 40 | * Extract a tar archive to the current directory. 41 | */ 42 | int tar_extract(const char *f); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /include/test.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_TEST_H 6 | #define KISS_TEST_H 7 | 8 | /** 9 | * Similar to assert() only it will not abort. 10 | */ 11 | #define test(x) test_internal(#x, !!(x), __LINE__) 12 | 13 | /** 14 | * Setup environment for testing. 15 | */ 16 | int test_begin(const char *file); 17 | 18 | /** 19 | * Code backing for test() macro. 20 | */ 21 | void test_internal(const char *expr, int result, int line); 22 | 23 | /** 24 | * Teardown environment for testing. 25 | */ 26 | int test_finish(void); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #ifndef KISS_UTIL_H 6 | #define KISS_UTIL_H 7 | 8 | #include 9 | 10 | /** 11 | * Turn bytes into human readable representation. 12 | */ 13 | char *human_readable(uint64_t n, char out[6]); 14 | 15 | /** 16 | * Run a command as a child and block until it completes. Returns 0 for 17 | * success and -1 for failure. 18 | */ 19 | int run_cmd(char *const argv[]); 20 | 21 | /** 22 | * Basic qsort callback to sort lists of strings. 23 | */ 24 | int qsort_cb_str(void const *a, void const *b); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /make: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | configure() { 4 | CFLAGS="-Wall -Wextra -pedantic $CFLAGS" 5 | CFLAGS="-std=c99 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 $CFLAGS" 6 | CPPFLAGS="-Iinclude $CPPFLAGS" 7 | 8 | _dep() { 9 | pkg-config --libs --cflags "${static:+--static}" "$1" || 10 | printf '%s\n' "$2" 11 | } 12 | 13 | # Detect when '-static' is passed via LDFLAGS and handle things accordingly. 14 | case " $LDFLAGS " in *" -static "*) 15 | static=1 16 | esac 17 | 18 | # Download functionality. Setting the environment variable CURL to '0' will 19 | # disable the package manager's ability to download sources. Git sources 20 | # will continue to work as normal (so long as git does). 21 | # 22 | # NOTE: The package manager does not depend on a specific SSL library. It 23 | # only directly depends on libcurl. 24 | case ${CURL:=1} in 1) 25 | LDFLAGS="$(_dep libcurl -lcurl) $LDFLAGS" 26 | CFLAGS="-DDL_USE_CURL $CFLAGS" 27 | esac 28 | 29 | # Which sha256 implementation to use. If all options are '0', an internal 30 | # sha256 implementation will be used. NOTE: Only one option may be enabled 31 | # at a time. 32 | { 33 | # SHA256 external implementation. Setting the environment variable 34 | # BEARSSL to '1' will cause the package manager to use sha256 from 35 | # bearssl. 36 | case ${BEARSSL:=0} in 1) 37 | LDFLAGS="$(_dep bearssl '-lbearssl') $LDFLAGS" 38 | CFLAGS="-DSHA256_USE_BEARSSL $CFLAGS" 39 | esac 40 | 41 | # SHA256 external implementation. Setting the environment variable 42 | # OPENSSL to '1' will cause the package manager to use sha256 from 43 | # openssl. 44 | case ${OPENSSL:=0} in 1) 45 | LDFLAGS="$(_dep openssl '-lssl -lcrypto') $LDFLAGS" 46 | CFLAGS="-DSHA256_USE_OPENSSL $CFLAGS" 47 | esac 48 | } 49 | 50 | # tar external implementation. Setting the environment variable 51 | # LIBARCHIVE to '1' will cause the package manager to use tar from 52 | # libarchive. If set to '0', the tar command will be executed. 53 | case ${LIBARCHIVE:=0} in 1) 54 | LDFLAGS="$(_dep libarchive '-larchive') $LDFLAGS" 55 | CFLAGS="-DTAR_USE_LIBARCHIVE $CFLAGS" 56 | esac 57 | } 58 | 59 | build() { 60 | configure 61 | 62 | _cc() { 63 | printf '%s %s\n' "${CC:=cc}" "$*" 64 | "$CC" "$@" || exit 1 65 | } 66 | 67 | for obj in src/[!k]*.c src/*/*.c; do 68 | _cc $CFLAGS $CPPFLAGS -c -o "${obj%%.c}.o" "$obj" 69 | done 70 | 71 | for prog in src/kiss.c test/*.c; do 72 | _cc $CFLAGS $CPPFLAGS -o "${prog%%.c}" "$prog" \ 73 | src/[!k]*.o src/*/*.o $LDFLAGS 74 | done 75 | } 76 | 77 | check() { 78 | export KISS_ROOT=$PWD/test/test_hier 79 | export KISS_PATH=$KISS_ROOT/repo/core:$KISS_ROOT/repo/extra 80 | export XDG_CACHE_HOME=$PWD/test/test_hier 81 | 82 | command -v valgrind && 83 | set -- valgrind \ 84 | --leak-check=full \ 85 | --track-origins=yes \ 86 | --error-exitcode=1 \ 87 | --suppressions=test/valgrind/musl.supp \ 88 | --suppressions=test/valgrind/lzma.supp 89 | 90 | for file in test/*.c; do 91 | "$@" "${file%%.c}" || exit 1 92 | done 93 | } 94 | 95 | compdb() { 96 | configure 97 | 98 | printf '[\n' 99 | for obj in src/*.c src/*/*.c; do 100 | cat < compile_commands.json 110 | 111 | "${1:-build}" || { 112 | printf 'error during %s\n' "$action" >&2 113 | exit 1 114 | } 115 | -------------------------------------------------------------------------------- /src/action.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "action.h" 4 | 5 | #include "arr.h" 6 | #include "cache.h" 7 | #include "error.h" 8 | #include "pkg.h" 9 | #include "repo.h" 10 | 11 | int state_init_pkg(struct state *s, char *p) { 12 | if (strstr(p, "/")) { 13 | err("invalid argument '%s'", p); 14 | return -1; 15 | } 16 | 17 | pkg *n = pkg_alloc(p); 18 | 19 | if (!n) { 20 | return -1; 21 | } 22 | 23 | if (s->opt & STATE_PKG_REPO) { 24 | if ((n->repo_fd = repo_find_pkg(s->repos, p)) == -1) { 25 | err_no("package '%s' not in any repository", p); 26 | pkg_free(n); 27 | return -1; 28 | } 29 | } 30 | 31 | if (s->opt & STATE_PKG_CACHE) { 32 | if (cache_init_pkg(&s->cache, p) < 0) { 33 | pkg_free(n); 34 | return -1; 35 | } 36 | } 37 | 38 | arr_push_b(s->pkgs, n); 39 | return 0; 40 | } 41 | 42 | static int state_init_repo_pwd(struct state *s) { 43 | if (buf_push_s(&s->mem, getenv("PWD")) == -EINVAL) { 44 | return -1; 45 | } 46 | 47 | size_t bn = buf_scan_rev(&s->mem, '/'); 48 | 49 | buf_set_len(s->mem, bn - 1); 50 | buf_push_c(&s->mem, ':'); 51 | buf_push_s(&s->mem, getenv("KISS_PATH")); 52 | 53 | if (setenv("KISS_PATH", s->mem, 1) == -1) { 54 | err_no("failed to set KISS_PATH"); 55 | return -1; 56 | } 57 | 58 | buf_set_len(s->mem, 0); 59 | return 0; 60 | } 61 | 62 | static int state_init_pkg_pwd(struct state *s) { 63 | if (buf_push_s(&s->mem, getenv("PWD")) == -EINVAL) { 64 | return -1; 65 | } 66 | 67 | size_t bn = buf_scan_rev(&s->mem, '/'); 68 | 69 | if (state_init_pkg(s, s->mem + bn) < 0) { 70 | return -1; 71 | } 72 | 73 | buf_set_len(s->mem, 0); 74 | return 0; 75 | } 76 | 77 | struct state *state_init(int argc, char *argv[], int opt) { 78 | struct state *s = calloc(sizeof *s, 1); 79 | 80 | if (!s) { 81 | return NULL; 82 | } 83 | 84 | s->opt = opt; 85 | 86 | if (opt & STATE_MEM) { 87 | if (!(s->mem = buf_alloc(0, 1024))) { 88 | goto error; 89 | } 90 | } 91 | 92 | if (opt & STATE_CACHE) { 93 | if (cache_init(&s->cache) < 0) { 94 | goto error; 95 | } 96 | } 97 | 98 | if (opt & STATE_REPO) { 99 | if (!(s->repos = arr_alloc(0, 12))) { 100 | goto error; 101 | } 102 | 103 | if (opt & STATE_KISS_PATH) { 104 | if (argc == 2 && state_init_repo_pwd(s) < 0) { 105 | goto error; 106 | } 107 | 108 | if (repo_open_PATH(s->repos, getenv("KISS_PATH")) < 0) { 109 | goto error; 110 | } 111 | } 112 | } 113 | 114 | if (opt & STATE_PKG) { 115 | if (!(s->pkgs = arr_alloc(0, (size_t) argc))) { 116 | goto error; 117 | } 118 | 119 | if ((opt & STATE_PKG_PWD) && argc == 2) { 120 | if (state_init_pkg_pwd(s) < 0) { 121 | goto error; 122 | } 123 | } 124 | 125 | for (int i = 2; i < argc; i++) { 126 | if (state_init_pkg(s, argv[i]) < 0) { 127 | goto error; 128 | } 129 | } 130 | } 131 | 132 | if (opt & STATE_ARGV) { 133 | if (!(s->argv = arr_alloc(0, (size_t) argc))) { 134 | goto error; 135 | } 136 | 137 | for (int i = 2; i < argc; i++) { 138 | arr_set_end(s->argv, argv[i]); 139 | } 140 | } 141 | 142 | return s; 143 | 144 | error: 145 | state_free(s); 146 | return NULL; 147 | } 148 | 149 | void state_free(struct state *s) { 150 | if (!s) { 151 | return; 152 | } 153 | 154 | if (s->opt & STATE_ARGV) { 155 | arr_free(s->argv); 156 | } 157 | 158 | if (s->opt & STATE_PKG) { 159 | pkg_free_all(s->pkgs); 160 | } 161 | 162 | if (s->opt & STATE_REPO) { 163 | repo_free_all(s->repos); 164 | } 165 | 166 | if (s->opt & STATE_CACHE) { 167 | cache_free(&s->cache); 168 | } 169 | 170 | if (s->opt & STATE_MEM) { 171 | buf_free(&s->mem); 172 | } 173 | 174 | free(s); 175 | } 176 | 177 | -------------------------------------------------------------------------------- /src/action/alt.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * Copyright (C) 2020 Dylan Araps 3 | **/ 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "action.h" 10 | #include "arr.h" 11 | #include "download.h" 12 | #include "error.h" 13 | #include "file.h" 14 | #include "pkg.h" 15 | #include "sha256.h" 16 | #include "util.h" 17 | 18 | static void print_alt(const char *n) { 19 | for (size_t f = 0; *n; n++) { 20 | if (*n == '>') { 21 | if (!f++) { 22 | putchar(' '); 23 | } 24 | 25 | putchar('/'); 26 | 27 | } else { 28 | putchar(*n); 29 | } 30 | } 31 | 32 | putchar('\n'); 33 | } 34 | 35 | static int list_alts(struct state *s, const char *db) { 36 | DIR *d = opendir(db); 37 | 38 | if (!d) { 39 | err_no("failed to open alt database"); 40 | return -1; 41 | } 42 | 43 | for (struct dirent *dp; (dp = read_dir(d)); ) { 44 | arr_push_b(s->argv, dp->d_name); 45 | } 46 | arr_sort(s->argv, qsort_cb_str); 47 | 48 | for (size_t i = 0; i < arr_len(s->argv); i++) { 49 | print_alt(s->argv[i]); 50 | } 51 | 52 | closedir(d); 53 | return 0; 54 | } 55 | 56 | static int swap_alt(struct state *s, const char *pkg, const char *alt) { 57 | if (!repo_has_pkg(s->repos[1], pkg)) { 58 | err("package '%s' not installed", pkg); 59 | return -1; 60 | } 61 | 62 | buf_set_len(s->mem, 0); 63 | buf_push_s(&s->mem, pkg); 64 | buf_push_s(&s->mem, alt); 65 | buf_fr_cg(&s->mem, 0, '/', '>'); 66 | 67 | struct stat sb; 68 | 69 | if (fstatat(s->repos[0]->fd, s->mem, &sb, AT_SYMLINK_NOFOLLOW) == -1) { 70 | err("alternative '%s %s' does not exist", pkg, alt); 71 | return -1; 72 | } 73 | 74 | if (access(alt, F_OK) == 0) { 75 | // find owner of file 76 | // copy / -> choices 77 | // update manifest of owner 78 | } 79 | 80 | // move choices -> / 81 | // update manifest of choice 82 | 83 | return 0; 84 | } 85 | 86 | int action_alt(struct state *s) { 87 | if (repo_open_db_push(s->repos, "choices") == -1) { 88 | err_no("failed to open alt database"); 89 | return -1; 90 | } 91 | 92 | if (arr_len(s->argv) == 0) { 93 | return list_alts(s, s->repos[0]->path); 94 | } 95 | 96 | if (arr_len(s->argv) == 1 && (s->argv[0][0] == '-' && !s->argv[0][1])) { 97 | // swap stdin 98 | return 0; 99 | } 100 | 101 | if (arr_len(s->argv) == 2) { 102 | if (s->argv[1][0] != '/' || !strstr(s->argv[1], "/")) { 103 | err("second argument ('%s') is invalid", s->argv[1]); 104 | return -1; 105 | } 106 | 107 | if (repo_open_db_push(s->repos, "installed") == -1) { 108 | err_no("failed to open pkg database"); 109 | return -1; 110 | } 111 | 112 | return swap_alt(s, s->argv[0], s->argv[1]); 113 | } 114 | 115 | err_no("invalid arguments"); 116 | return -1; 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/action/build.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * Copyright (C) 2020 Dylan Araps 3 | **/ 4 | #include 5 | #include 6 | #include 7 | 8 | #include "action.h" 9 | #include "arr.h" 10 | #include "download.h" 11 | #include "error.h" 12 | #include "file.h" 13 | #include "pkg.h" 14 | #include "sha256.h" 15 | 16 | static int pkg_verify_sources(buf *m, pkg *p) { 17 | FILE *f = pkg_fopen(p, "checksums", O_RDONLY, "r"); 18 | 19 | if (!f && errno == ENOENT) { 20 | err_no("[%s] checksums file missing", p->name); 21 | return -1; 22 | } 23 | 24 | if (!f) { 25 | err_no("[%s] failed to open checksums file", p->name); 26 | return -1; 27 | } 28 | 29 | for (; buf_getline(&m, f, 256) == 0; buf_set_len(m, 0)) { 30 | if (m[0] == 'S' && m[1] == 'K' && m[2] == 'I' && m[3] == 'P' && !m[4]) { 31 | continue; 32 | } 33 | 34 | printf("%s\n", m); 35 | } 36 | 37 | fclose(f); 38 | return 0; 39 | } 40 | 41 | int action_build(struct state *s) { 42 | int ret = action_download(s); 43 | 44 | if (ret < 0) { 45 | return ret; 46 | } 47 | 48 | for (size_t i = 0; i < arr_len(s->pkgs); i++) { 49 | if ((ret = pkg_verify_sources(s->mem, s->pkgs[i])) < 0) { 50 | return ret; 51 | } 52 | } 53 | 54 | return 0; 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/action/checksum.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * Copyright (C) 2020 Dylan Araps 3 | **/ 4 | #include 5 | #include 6 | #include 7 | 8 | #include "action.h" 9 | #include "arr.h" 10 | #include "download.h" 11 | #include "error.h" 12 | #include "file.h" 13 | #include "pkg.h" 14 | #include "sha256.h" 15 | 16 | static int parse_source_file(struct state *s, pkg *p, FILE *f, FILE *d) { 17 | int parsed = 0; 18 | 19 | for (; buf_getline(&s->mem, f, 256) == 0; buf_set_len(s->mem, 0)) { 20 | if (!*s->mem || *s->mem == '\n' || *s->mem == '#') { 21 | continue; 22 | } 23 | 24 | char *f2 = s->mem + buf_scan(&s->mem, 0, ' '); 25 | FILE *src = 0; 26 | 27 | switch (pkg_source_type(p, s->mem)) { 28 | case SRC_ABS: 29 | src = fopen(s->mem, "r"); 30 | break; 31 | 32 | case SRC_REL: 33 | src = pkg_fopen(p, s->mem, O_RDONLY, "r"); 34 | break; 35 | 36 | case SRC_URL: 37 | while (*f2 && *f2 == '/') f2++; 38 | src = cache_fopen(s->cache.fd[CAC_SRC], 39 | p->name, f2, strrchr(s->mem, '/') + 1, O_RDONLY, "r"); 40 | break; 41 | 42 | case SRC_GIT: 43 | continue; 44 | } 45 | 46 | if (!src) { 47 | err_no("failed to open source '%s'", s->mem); 48 | return -1; 49 | } 50 | 51 | unsigned char hash[SHA256_LEN]; 52 | sha256_file(hash, src); 53 | fclose(src); 54 | char hash_string[65]; 55 | sha256_to_string(hash, hash_string); 56 | fprintf(d, "%s\n", hash_string); 57 | puts(hash_string); 58 | parsed++; 59 | } 60 | 61 | return parsed; 62 | } 63 | 64 | int action_checksum(struct state *s) { 65 | int ret = action_download(s); 66 | 67 | if (ret < 0) { 68 | return ret; 69 | } 70 | 71 | for (size_t i = 0; i < arr_len(s->pkgs); i++) { 72 | FILE *src = pkg_fopen(s->pkgs[i], "sources", O_RDONLY, "r"); 73 | 74 | if (!src && errno == ENOENT) { 75 | err("[%s] no sources file, skipping", s->pkgs[i]->name); 76 | continue; 77 | } 78 | 79 | if (!src) { 80 | err_no("failed to open sources file"); 81 | return -1; 82 | } 83 | 84 | FILE *chk = pkg_fopen(s->pkgs[i], "checksums", O_RDWR | O_CREAT, "w"); 85 | 86 | if (!chk) { 87 | err_no("[%s] failed to open checksums file", s->pkgs[i]->name); 88 | fclose(src); 89 | return -1; 90 | } 91 | 92 | msg("[%zu/%zu] generating checksums for [%s]", 93 | i + 1, arr_len(s->pkgs), s->pkgs[i]->name); 94 | 95 | ret = parse_source_file(s, s->pkgs[i], src, chk); 96 | 97 | fclose(src); 98 | fclose(chk); 99 | 100 | switch (ret) { 101 | case 0: 102 | puts("nothing to do"); 103 | break; 104 | 105 | case -1: 106 | return -1; 107 | 108 | default: 109 | puts("updated checksums file"); 110 | } 111 | } 112 | 113 | return 0; 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/action/download.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * Copyright (C) 2020 Dylan Araps 3 | **/ 4 | #include 5 | #include 6 | #include 7 | 8 | #include "action.h" 9 | #include "arr.h" 10 | #include "download.h" 11 | #include "error.h" 12 | #include "file.h" 13 | #include "pkg.h" 14 | 15 | static size_t source_dest_path(struct state *s, 16 | const char *n, const char *d, const char *f) { 17 | size_t mem_pre = buf_len(s->mem) + 1; 18 | buf_printf(&s->mem, "%c%s../../sources/%s/", 0, s->cache.dir, n); 19 | 20 | if (d) { 21 | buf_push_s(&s->mem, d); 22 | buf_rstrip(&s->mem, '/'); 23 | buf_push_c(&s->mem, '/'); 24 | } 25 | 26 | buf_push_s(&s->mem, strrchr(f, '/') + 1); 27 | return mem_pre; 28 | } 29 | 30 | static int download(const char *url, const char *dest) { 31 | FILE *src_file = fopen(dest, "w"); 32 | 33 | if (!src_file) { 34 | err_no("failed to open source '%s'", dest); 35 | return -1; 36 | } 37 | 38 | int err = source_download(url, src_file); 39 | fclose(src_file); 40 | 41 | if (err < 0) { 42 | unlink(dest); 43 | } 44 | 45 | return err; 46 | } 47 | 48 | static int parse_source_file(struct state *s, pkg *p, FILE *f) { 49 | int parsed = 0; 50 | 51 | for (; buf_getline(&s->mem, f, 256) == 0; buf_set_len(s->mem, 0)) { 52 | if (!*s->mem || *s->mem == '\n' || *s->mem == '#') { 53 | continue; 54 | } 55 | 56 | char *f2 = s->mem + buf_scan(&s->mem, 0, ' '); 57 | 58 | switch (pkg_source_type(p, s->mem)) { 59 | case SRC_URL: 60 | while (*f2 && *f2 == '/') f2++; 61 | 62 | char *dest = s->mem + source_dest_path(s, p->name, f2, s->mem); 63 | 64 | if (access(dest, F_OK) == 0) { 65 | continue; 66 | } 67 | 68 | if (cache_mkdirat(s->cache.fd[CAC_SRC], p->name, f2) < 0) { 69 | return -1; 70 | } 71 | 72 | if (download(s->mem, dest) < 0) { 73 | return -1; 74 | } 75 | 76 | parsed++; 77 | break; 78 | 79 | case -1: 80 | err_no("source '%s' not found", s->mem); 81 | return -1; 82 | } 83 | } 84 | 85 | return parsed; 86 | } 87 | 88 | int action_download(struct state *s) { 89 | int err = 0; 90 | 91 | for (size_t i = 0; i < arr_len(s->pkgs); i++) { 92 | FILE *src = pkg_fopen(s->pkgs[i], "sources", O_RDONLY, "r"); 93 | 94 | if (!src && errno == ENOENT) { 95 | err("[%s] no sources file, skipping", s->pkgs[i]->name); 96 | continue; 97 | } 98 | 99 | if (!src) { 100 | err_no("failed to open sources file"); 101 | err = -1; 102 | goto error; 103 | } 104 | 105 | msg("[%zu/%zu] downloading sources for [%s]", 106 | i + 1, arr_len(s->pkgs), s->pkgs[i]->name); 107 | 108 | int parsed = parse_source_file(s, s->pkgs[i], src); 109 | fclose(src); 110 | 111 | if (parsed == -1) { 112 | err = -1; 113 | goto error; 114 | } 115 | 116 | if (parsed == 0) { 117 | puts("nothing to do"); 118 | } 119 | } 120 | 121 | error: 122 | source_curl_cleanup(); 123 | return err; 124 | } 125 | 126 | -------------------------------------------------------------------------------- /src/action/list.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * Copyright (C) 2020 Dylan Araps 3 | **/ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "action.h" 13 | #include "arr.h" 14 | #include "buf.h" 15 | #include "error.h" 16 | #include "file.h" 17 | #include "pkg.h" 18 | #include "repo.h" 19 | #include "util.h" 20 | 21 | static int db_to_list(struct state *s, const char *db) { 22 | DIR *d = opendir(db); 23 | 24 | if (!d) { 25 | err_no("failed to open database"); 26 | return -1; 27 | } 28 | 29 | for (struct dirent *dp; (dp = read_dir(d)); ) { 30 | if (state_init_pkg(s, dp->d_name) < 0) { 31 | closedir(d); 32 | return -1; 33 | } 34 | } 35 | 36 | arr_sort(s->pkgs, pkg_sort_name); 37 | closedir(d); 38 | return 0; 39 | } 40 | 41 | static FILE *open_version(int fd, const char *pkg) { 42 | FILE *ver = fopenatat(fd, pkg, "version", O_RDONLY, "r"); 43 | 44 | if (!ver) { 45 | if (errno == ENOENT) { 46 | err_no("package '%s' not installed", pkg); 47 | 48 | } else { 49 | err_no("[%s] failed to open version file", pkg); 50 | } 51 | 52 | return NULL; 53 | } 54 | 55 | return ver; 56 | } 57 | 58 | static int read_version(struct state *s, int fd, const char *pkg) { 59 | FILE *f = open_version(fd, pkg); 60 | 61 | if (!f) { 62 | return -1; 63 | } 64 | 65 | int err = buf_getline(&s->mem, f, 32); 66 | 67 | if (err < 0) { 68 | err_no("[%s] failed to read version file", pkg); 69 | } 70 | 71 | fclose(f); 72 | return err < 0 ? -1 : 0; 73 | } 74 | 75 | int action_list(struct state *s) { 76 | struct repo *db = repo_open_db("installed"); 77 | 78 | if (!db) { 79 | err_no("failed to open database"); 80 | return -1; 81 | } 82 | 83 | int err = 0; 84 | 85 | if (arr_len(s->pkgs) == 0 && (err = db_to_list(s, db->path)) < 0) { 86 | err_no("failed to read database"); 87 | goto error; 88 | } 89 | 90 | for (size_t i = 0; i < arr_len(s->pkgs); i++) { 91 | buf_set_len(s->mem, 0); 92 | 93 | if ((err = read_version(s, db->fd, s->pkgs[i]->name)) < 0) { 94 | goto error; 95 | } 96 | 97 | size_t rel = buf_scan(&s->mem, 0, ' '); 98 | 99 | fprintf(stdout, "%s %s %s\n", 100 | s->pkgs[i]->name, s->mem, s->mem + rel); 101 | } 102 | 103 | error: 104 | repo_free(db); 105 | return err; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /src/action/search.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * Copyright (C) 2020 Dylan Araps 3 | **/ 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "action.h" 10 | #include "arr.h" 11 | #include "buf.h" 12 | #include "error.h" 13 | #include "repo.h" 14 | 15 | int action_search(struct state *s) { 16 | glob_t g = { .gl_pathc = 0, }; 17 | 18 | for (size_t i = 0; i < arr_len(s->pkgs); i++) { 19 | size_t glob_pre = g.gl_pathc; 20 | 21 | for (size_t j = 0; j < arr_len(s->repos); j++) { 22 | buf_set_len(s->mem, 0); 23 | buf_printf(&s->mem, "%s/%s/", 24 | s->repos[j]->path, s->pkgs[i]->name); 25 | 26 | switch (glob(s->mem, g.gl_pathc ? GLOB_APPEND : 0, NULL, &g)) { 27 | case GLOB_NOSPACE: 28 | case GLOB_ABORTED: 29 | err("glob encountered error with query '%s'", s->mem); 30 | goto glob_error; 31 | } 32 | } 33 | 34 | if ((g.gl_pathc - glob_pre) == 0) { 35 | err("no search results for '%s'", s->pkgs[i]->name); 36 | goto glob_error; 37 | } 38 | } 39 | 40 | for (size_t i = 0; i < g.gl_pathc; i++) { 41 | puts(g.gl_pathv[i]); 42 | } 43 | 44 | globfree(&g); 45 | return 0; 46 | 47 | glob_error: 48 | globfree(&g); 49 | return -1; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/arr.c: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #include 6 | 7 | #include "arr.h" 8 | 9 | void *arr_alloc(void *a, size_t l) { 10 | size_t *p = a ? arr_raw(a) : 0; 11 | size_t *n = realloc(p, (sizeof(*n) * (l + 2))); 12 | 13 | if (!n) { 14 | return NULL; 15 | } 16 | 17 | n[0] = l; 18 | n[1] = p ? n[1] : 0; 19 | 20 | return (void *) &n[2]; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/buf.c: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "error.h" 12 | #include "buf.h" 13 | 14 | buf *buf_alloc(buf **s, size_t l) { 15 | size_t *p = buf_raw(s); 16 | size_t *n = realloc(p, (sizeof (size_t) * 2) + l); 17 | 18 | if (!n) { 19 | return NULL; 20 | } 21 | 22 | n[0] = l; 23 | n[1] = p ? n[1] : 0; 24 | 25 | return (char *) &n[2]; 26 | } 27 | 28 | static int buf_alloc_maybe(buf **s, size_t l) { 29 | if ((buf_len(*s) + l) <= buf_cap(*s)) { 30 | return 0; 31 | } 32 | 33 | buf *n = buf_alloc(s, buf_cap(*s) + (l + (l >> 1))); 34 | 35 | if (!n) { 36 | return -ENOMEM; 37 | } 38 | 39 | *s = n; 40 | return 0; 41 | } 42 | 43 | int buf_push_l(buf **s, const char *d, size_t l) { 44 | if (buf_alloc_maybe(s, l) < 0) { 45 | return -ENOMEM; 46 | } 47 | 48 | memcpy(*s + buf_len(*s), d, l + 1); 49 | buf_set_len(*s, buf_len(*s) + l); 50 | return 0; 51 | } 52 | 53 | int buf_push_s(buf **s, const char *d) { 54 | if (!d || !*d) { 55 | return -EINVAL; 56 | } 57 | 58 | return buf_push_l(s, d, strlen(d)); 59 | } 60 | 61 | int buf_push_c(buf **s, int d) { 62 | if (buf_alloc_maybe(s, 1) < 0) { 63 | return -ENOMEM; 64 | } 65 | 66 | (*s)[buf_len(*s)] = d; 67 | buf_set_len(*s, buf_len(*s) + 1); 68 | return 0; 69 | } 70 | 71 | int buf_undo_c(buf **s, int d) { 72 | if (buf_end(*s) == d) { 73 | buf_set_len(*s, buf_len(*s) - 1); 74 | return 0; 75 | } 76 | 77 | return -1; 78 | } 79 | 80 | void buf_rstrip(buf **s, int d) { 81 | while (buf_undo_c(s, d) == 0); 82 | } 83 | 84 | int buf_set(buf **s, int c, size_t l) { 85 | if (buf_alloc_maybe(s, l + 1) < 0) { 86 | return -ENOMEM; 87 | } 88 | 89 | memset(*s + buf_len(*s), c, l); 90 | buf_set_len(*s, buf_len(*s) + l); 91 | return 0; 92 | } 93 | 94 | int buf_getline(buf **s, FILE *f, size_t l) { 95 | do { 96 | if (ferror(f) || feof(f)) { 97 | return -1; 98 | } 99 | 100 | if (buf_alloc_maybe(s, l) < 0) { 101 | return -ENOMEM; 102 | } 103 | 104 | if (!fgets(*s + buf_len(*s), l, f)) { 105 | return -1; 106 | } 107 | 108 | buf_set_len(*s, buf_len(*s) + strnlen(*s + buf_len(*s), l)); 109 | 110 | } while (buf_end(*s) != '\n'); 111 | 112 | buf_set_len(*s, buf_len(*s) - 1); 113 | return 0; 114 | } 115 | 116 | static int buf_vprintf(buf **s, const char *f, va_list ap) { 117 | va_list ap2; 118 | va_copy(ap2, ap); 119 | int l1 = vsnprintf(NULL, 0, f, ap2); 120 | va_end(ap2); 121 | 122 | if (l1 <= 0) { 123 | return -1; 124 | } 125 | 126 | if (buf_alloc_maybe(s, (size_t) l1) < 0) { 127 | return -ENOMEM; 128 | } 129 | 130 | if (vsnprintf(*s + buf_len(*s), (size_t) l1 + 1, f, ap) != l1) { 131 | return -1; 132 | } 133 | 134 | buf_set_len(*s, buf_len(*s) + (size_t) l1); 135 | return 0; 136 | } 137 | 138 | int buf_printf(buf **s, const char *f, ...) { 139 | va_list ap; 140 | va_start(ap, f); 141 | int ret = buf_vprintf(s, f, ap); 142 | va_end(ap); 143 | return ret; 144 | } 145 | 146 | size_t buf_scan(buf **s, size_t l, int c) { 147 | size_t i = l; 148 | 149 | for (; i < buf_len(*s); i++) { 150 | if ((*s)[i] == c) { 151 | (*s)[i] = 0; 152 | i++; 153 | break; 154 | } 155 | } 156 | 157 | return i; 158 | } 159 | 160 | size_t buf_scan_rev(buf **s, int c) { 161 | size_t i = buf_len(*s); 162 | 163 | for (; i; i--) { 164 | if ((*s)[i] == c) { 165 | (*s)[i] = 0; 166 | i++; 167 | break; 168 | } 169 | } 170 | 171 | return i; 172 | } 173 | 174 | int buf_fr_c(buf **s, size_t l, char f, char r) { 175 | for (char *t = *s + l; *t; t++) { 176 | if (*t == f) { 177 | *t = r; 178 | return 1; 179 | } 180 | } 181 | 182 | return 0; 183 | } 184 | 185 | void buf_fr_cg(buf **s, size_t l, char f, char r) { 186 | while (buf_fr_c(s, l, f, r)); 187 | } 188 | 189 | void buf_free(buf **s) { 190 | free(buf_raw(s)); 191 | } 192 | 193 | -------------------------------------------------------------------------------- /src/cache.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "buf.h" 8 | #include "file.h" 9 | #include "error.h" 10 | #include "cache.h" 11 | 12 | static const char *caches[] = { 13 | "build", 14 | "pkg", 15 | "extract", 16 | 17 | "../../sources", 18 | "../../logs", 19 | "../../bin", 20 | }; 21 | 22 | int cache_init(struct cache *c) { 23 | c->dir = buf_alloc(0, 256); 24 | 25 | if (!c->dir) { 26 | return -ENOMEM; 27 | } 28 | 29 | if (cache_get_base(&c->dir) < 0) { 30 | return -1; 31 | } 32 | 33 | if (cache_init_all(c) < 0) { 34 | return -1; 35 | } 36 | 37 | return 0; 38 | } 39 | 40 | int cache_init_all(struct cache *c) { 41 | if (mkdir_p(c->dir, 0755) < 0) { 42 | return -1; 43 | } 44 | 45 | if ((c->fd[6] = open(c->dir, O_RDONLY)) == -1) { 46 | err_no("failed to open directory '%s'", c->dir); 47 | return -1; 48 | } 49 | 50 | for (size_t i = 0; i < CAC_DIR; i++) { 51 | if (mkdirat(c->fd[6], caches[i], 0755) == -1 && errno != EEXIST) { 52 | err_no("failed to create directory '%s%s'", c->dir, caches[i]); 53 | return -1; 54 | } 55 | 56 | if ((c->fd[i] = openat(c->fd[6], caches[i], O_RDONLY)) == -1) { 57 | err_no("failed to open directory '%s%s'", c->dir, caches[i]); 58 | return -1; 59 | } 60 | } 61 | 62 | return 0; 63 | } 64 | 65 | int cache_init_pkg(struct cache *c, const char *pkg) { 66 | for (size_t i = 0; i < CAC_DIR; i++) { 67 | if (mkdirat(c->fd[i], pkg, 0755) == -1 && errno != EEXIST) { 68 | err_no("failed to create directory '%s%s/%s'", 69 | c->dir, caches[i], pkg); 70 | return -1; 71 | } 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | int cache_get_base(buf **c) { 78 | int err = buf_push_s(c, getenv("KISS_TMPDIR")); 79 | 80 | if (err == -EINVAL) { 81 | err = buf_push_s(c, getenv("XDG_CACHE_HOME")); 82 | 83 | if (err == -EINVAL) { 84 | err = buf_push_s(c, getenv("HOME")); 85 | 86 | if (err == -EINVAL) { 87 | err("failed to find cache directory"); 88 | return -1; 89 | } 90 | 91 | buf_rstrip(c, '/'); 92 | buf_push_l(c, "/.cache", 7); 93 | } 94 | } 95 | 96 | buf_rstrip(c, '/'); 97 | return buf_printf(c, "/kiss/proc/%ld/", (long) getpid()); 98 | } 99 | 100 | FILE *cache_fopen(int fd, const char *pkg, const char *des, const char *f, 101 | int M, const char *m) { 102 | int fd2 = openat(fd, pkg, O_RDONLY); 103 | 104 | if (fd2 == -1) { 105 | return NULL; 106 | } 107 | 108 | int fd3 = fd2; 109 | 110 | if (des && *des) { 111 | fd3 = openat(fd2, des, O_RDONLY); 112 | close(fd2); 113 | } 114 | 115 | if (fd3 == -1) { 116 | return NULL; 117 | } 118 | 119 | int fd4 = openat(fd3, f, M); 120 | close(fd3); 121 | 122 | return fdopen(fd4, m); 123 | } 124 | 125 | int cache_mkdirat(int fd, const char *pkg, const char *des) { 126 | if (!des || !*des) { 127 | return 0; 128 | } 129 | 130 | int pfd = openat(fd, pkg, O_RDONLY); 131 | 132 | if (pfd == -1) { 133 | return -1; 134 | } 135 | 136 | int ret = mkdirat(pfd, des, 0755); 137 | close(pfd); 138 | 139 | if (ret == -1 && errno != EEXIST) { 140 | return -1; 141 | } 142 | 143 | return 0; 144 | } 145 | 146 | int cache_clean(struct cache *c) { 147 | return rm_rf(c->dir); 148 | } 149 | 150 | void cache_free(struct cache *c) { 151 | for (size_t i = 0; i < CAC_DIR; i++) { 152 | close(c->fd[i]); 153 | } 154 | 155 | buf_free(&c->dir); 156 | } 157 | 158 | -------------------------------------------------------------------------------- /src/download.c: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef DL_USE_CURL 10 | #include 11 | #endif 12 | 13 | #include "download.h" 14 | #include "error.h" 15 | #include "util.h" 16 | 17 | /** 18 | * CURL is used to download remote sources. This dependency can be disabled 19 | * by setting the environment variable CURL to 0 prior to the build. Disabling 20 | * this dependency will also disable the download feature. 21 | */ 22 | #ifdef DL_USE_CURL 23 | 24 | // same handle is used for all requests 25 | static CURL *curl; 26 | 27 | // catch Ctrl+C 28 | static volatile sig_atomic_t sigint; 29 | 30 | static void handle_sigint(int sig) { 31 | (void) sig; 32 | sigint = 1; 33 | } 34 | 35 | static int status(void *file, curl_off_t dl_tot, curl_off_t dl_cur, 36 | curl_off_t ul_tot, curl_off_t ul_cur) { 37 | (void) ul_tot; 38 | (void) ul_cur; 39 | 40 | if (dl_cur < 1 || dl_tot <= 1 || dl_tot < dl_cur) { 41 | return 0; 42 | } 43 | 44 | if (sigint) { 45 | return -1; 46 | } 47 | 48 | #define BAR_LEN 25 49 | const int cur = dl_cur * BAR_LEN / dl_tot % (BAR_LEN + 1); 50 | 51 | fprintf(stderr, "%-40.40s %5s / %5s [%.*s%*s] %3d%%\r", 52 | (char *) file, 53 | human_readable(dl_cur, (char [6]){0}), 54 | human_readable(dl_tot, (char [6]){0}), 55 | cur, "================================", 56 | BAR_LEN - cur, "", 57 | (int) ((dl_cur * 100) / dl_tot) % 101 58 | ); 59 | 60 | fflush(stderr); 61 | return 0; 62 | } 63 | 64 | static int source_curl_init(void) { 65 | CURLcode ret = curl_global_init(CURL_GLOBAL_ALL); 66 | 67 | if (ret != 0) { 68 | err("failed to initialize curl: %s", curl_easy_strerror(ret)); 69 | return -1; 70 | } 71 | 72 | if (!(curl = curl_easy_init())) { 73 | err("failed to initialize curl"); 74 | goto error; 75 | } 76 | 77 | struct sigaction act = { .sa_handler = handle_sigint }; 78 | 79 | if (sigaction(SIGINT, &act, NULL) == -1) { 80 | err_no("sigaction failed"); 81 | goto error; 82 | } 83 | 84 | return 0; 85 | 86 | error: 87 | curl_global_cleanup(); 88 | return -1; 89 | } 90 | 91 | static CURLcode source_curl_setopts(void) { 92 | CURLcode ret = 0; 93 | 94 | if ((ret = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L)) != 0) { 95 | err("CURLOPT_NOPROGRESS: %s", curl_easy_strerror(ret)); 96 | return ret; 97 | } 98 | 99 | if ((ret = curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L)) != 0) { 100 | err("CURLOPT_TCP_KEEPALIVE: %s", curl_easy_strerror(ret)); 101 | return ret; 102 | } 103 | 104 | if ((ret = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L)) != 0) { 105 | err("CURLOPT_FOLLOWLOCATION: %s", curl_easy_strerror(ret)); 106 | return ret; 107 | } 108 | 109 | if ((ret = curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, status)) != 0) { 110 | err("CURLOPT_XFERINFOFUNCTION: %s", curl_easy_strerror(ret)); 111 | return ret; 112 | } 113 | 114 | return ret; 115 | } 116 | 117 | static CURLcode source_curl_stage(const char *url, FILE *dest) { 118 | CURLcode ret = 0; 119 | 120 | if ((ret = curl_easy_setopt(curl, CURLOPT_URL, url)) != 0) { 121 | err("CURLOPT_URL: %s", curl_easy_strerror(ret)); 122 | return ret; 123 | } 124 | 125 | if ((ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, dest)) != 0) { 126 | err("CURLOPT_WRITEDATA: %s", curl_easy_strerror(ret)); 127 | return ret; 128 | } 129 | 130 | char *basename = strrchr(url, '/') + 1; 131 | 132 | if ((ret = curl_easy_setopt(curl, CURLOPT_XFERINFODATA, basename)) != 0) { 133 | err("CURLOPT_XFERINFODATA: %s", curl_easy_strerror(ret)); 134 | return ret; 135 | } 136 | 137 | return ret; 138 | } 139 | 140 | int source_download(const char *url, FILE *dest) { 141 | if (!curl) { 142 | if (source_curl_init() != 0) { 143 | return -1; 144 | } 145 | 146 | if (source_curl_setopts() != 0) { 147 | return -1; 148 | } 149 | } 150 | 151 | if (source_curl_stage(url, dest) != 0) { 152 | return -1; 153 | } 154 | 155 | CURLcode ret = curl_easy_perform(curl); 156 | 157 | // final status newline 158 | fputc('\n', stdout); 159 | 160 | if (ret != 0) { 161 | err("failed to download file '%s': %s", url, curl_easy_strerror(ret)); 162 | return -1; 163 | } 164 | 165 | return 0; 166 | } 167 | 168 | void source_curl_cleanup(void) { 169 | if (curl) { 170 | curl_easy_cleanup(curl); 171 | curl_global_cleanup(); 172 | } 173 | } 174 | 175 | /** 176 | * Fallback stubs for public functions when CURL dependency has been disabled. 177 | * In the future, the build system may simply exclude this file from compilation 178 | * with ifdefs used around the caller. 179 | */ 180 | #else 181 | 182 | int source_download(const char *url, FILE *dest) { 183 | (void) url; (void) dest; 184 | 185 | err("package manager compiled without download support"); 186 | return -ENOSYS; 187 | } 188 | 189 | void source_curl_cleanup(void) { 190 | // 191 | } 192 | 193 | #endif 194 | 195 | -------------------------------------------------------------------------------- /src/file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "error.h" 10 | #include "file.h" 11 | 12 | int mkdir_p(const char *path, mode_t m) { 13 | for (char *p = strchr(path + 1, '/'); p; p = strchr(p + 1, '/')) { 14 | *p = 0; 15 | int ret = mkdir(path, m); 16 | *p = '/'; 17 | 18 | if (ret == -1 && errno != EEXIST) { 19 | err_no("failed to create directory '%s'", path); 20 | return -1; 21 | } 22 | } 23 | 24 | return 0; 25 | } 26 | 27 | static int rm_cb(const char *p, const struct stat *sb, int t, struct FTW *b) { 28 | (void) sb; 29 | (void) t; 30 | (void) b; 31 | 32 | if (remove(p) == -1) { 33 | err_no("failed to remove file '%s'", p); 34 | return -1; 35 | } 36 | 37 | return 0; 38 | } 39 | 40 | int rm_rf(const char *path) { 41 | return nftw(path, rm_cb, 64, FTW_DEPTH | FTW_PHYS); 42 | } 43 | 44 | FILE *fopenat(int fd, const char *p, int m, const char *M) { 45 | int fd2 = openat(fd, p, m); 46 | 47 | if (fd2 == -1) { 48 | return NULL; 49 | } 50 | 51 | return fdopen(fd2, M); 52 | } 53 | 54 | FILE *fopenatat(int fd, const char *p, const char *f, int m, const char *M) { 55 | int pfd = openat(fd, p, O_RDONLY); 56 | 57 | if (pfd == -1) { 58 | return NULL; 59 | } 60 | 61 | int ffd = openat(pfd, f, m, 0644); 62 | close(pfd); 63 | 64 | if (ffd == -1) { 65 | return NULL; 66 | } 67 | 68 | return fdopen(ffd, M); 69 | } 70 | 71 | struct dirent *read_dir(DIR *d) { 72 | struct dirent *r = readdir(d); 73 | 74 | if (r && r->d_name[0] == '.') { 75 | if (!r->d_name[1] || (r->d_name[1] == '.' && !r->d_name[2])) { 76 | return read_dir(d); 77 | } 78 | } 79 | 80 | return r; 81 | } 82 | -------------------------------------------------------------------------------- /src/kiss.c: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-License-Identifier: MIT 3 | * Copyright (C) 2020 Dylan Araps 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #include "action.h" 10 | #include "arr.h" 11 | #include "buf.h" 12 | #include "cache.h" 13 | #include "error.h" 14 | #include "pkg.h" 15 | #include "repo.h" 16 | 17 | static void version(char *arg0) { 18 | msg("%s 0.0.1 (compiled %s)", arg0, __DATE__); 19 | } 20 | 21 | static void usage(char *arg0) { 22 | fputs(arg0, stdout); 23 | fputs(" [a|b|c|d|i|l|r|s|u|v] [pkg]...\n", stdout); 24 | 25 | puts("alt List and swap to alternatives"); 26 | puts("build Build a package"); 27 | puts("checksum Generate checksums"); 28 | puts("download Pre-download all sources"); 29 | puts("install Install a package"); 30 | puts("list List installed packages"); 31 | puts("remove Remove a package"); 32 | puts("search Search for a package"); 33 | puts("update Update the system"); 34 | puts("version Package manager version"); 35 | } 36 | 37 | static int run_extension(char *argv[]) { 38 | char ext[256] = "kiss-"; 39 | 40 | strncat(ext, *argv, 255); 41 | execvp(ext, argv); 42 | 43 | err("failed to execute extension %s", ext); 44 | return -1; 45 | } 46 | 47 | int main (int argc, char *argv[]) { 48 | struct state *s = 0; 49 | int err = 0; 50 | 51 | if (argc < 2 || !argv[1] || !argv[1][0] || argv[1][0] == '-') { 52 | usage(argv[0]); 53 | 54 | // Check if argument matches an action. True for b==build and build==build 55 | // strcmp is only reached when both first characters match. 56 | #define ARG(a, b) ((*(a)) == (*(b)) && ((!(a)[1]) || strcmp(a, b) == 0)) 57 | 58 | } else if (ARG(argv[1], "alt")) { 59 | s = state_init(argc, argv, STATE_ARGV | STATE_MEM | STATE_REPO); 60 | err = s ? action_alt(s) : -1; 61 | 62 | } else if (ARG(argv[1], "build")) { 63 | s = state_init(argc, argv, STATE_ALL); 64 | err = s ? action_build(s) : -1; 65 | 66 | } else if (ARG(argv[1], "checksum")) { 67 | s = state_init(argc, argv, STATE_ALL); 68 | err = s ? action_checksum(s) : -1; 69 | 70 | } else if (ARG(argv[1], "download")) { 71 | s = state_init(argc, argv, STATE_ALL); 72 | err = s ? action_download(s) : -1; 73 | 74 | } else if (ARG(argv[1], "list")) { 75 | s = state_init(argc, argv, STATE_PKG | STATE_MEM); 76 | err = s ? action_list(s) : -1; 77 | 78 | } else if (ARG(argv[1], "search")) { 79 | s = state_init(argc, argv, STATE_SEARCH); 80 | err = s ? action_search(s) : -1; 81 | 82 | } else if (ARG(argv[1], "version")) { 83 | version(argv[0]); 84 | 85 | } else { 86 | err = run_extension(argv + 1); 87 | } 88 | 89 | state_free(s); 90 | return err; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/pkg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "arr.h" 9 | #include "buf.h" 10 | #include "pkg.h" 11 | 12 | pkg *pkg_alloc(const char *name) { 13 | pkg *n = malloc(sizeof *n); 14 | 15 | if (n) { 16 | n->repo_fd = 0; 17 | n->name = strdup(name); 18 | 19 | if (n->name) { 20 | return n; 21 | } 22 | 23 | free(n); 24 | } 25 | 26 | return NULL; 27 | } 28 | 29 | void pkg_free(pkg *p) { 30 | free(p->name); 31 | free(p); 32 | } 33 | 34 | void pkg_free_all(pkg **p) { 35 | for (size_t i = 0; i < arr_len(p); i++) { 36 | pkg_free(p[i]); 37 | } 38 | 39 | arr_free(p); 40 | } 41 | 42 | FILE *pkg_fopen(pkg *p, const char *f, int M, const char *m) { 43 | int pfd = openat(p->repo_fd, p->name, O_RDONLY); 44 | 45 | if (pfd == -1) { 46 | return NULL; 47 | } 48 | 49 | int ffd = openat(pfd, f, M, 0644); 50 | close(pfd); 51 | 52 | if (ffd == -1) { 53 | return NULL; 54 | } 55 | 56 | return fdopen(ffd, m); 57 | } 58 | 59 | int pkg_faccessat(int repo_fd, const char *pkg, const char *file) { 60 | int pfd = openat(repo_fd, pkg, O_RDONLY); 61 | 62 | if (pfd == -1) { 63 | return -1; 64 | } 65 | 66 | int ret = faccessat(pfd, file, F_OK, 0); 67 | close(pfd); 68 | return ret; 69 | } 70 | 71 | int pkg_source_type(pkg *p, char *src) { 72 | if (src[0] == 'g' && src[1] == 'i' && src[2] == 't' && src[3] == '+') { 73 | return SRC_GIT; 74 | } 75 | 76 | if (src[0] == '/') { 77 | return access(src, F_OK) == 0 ? SRC_ABS : -1; 78 | } 79 | 80 | if (strstr(src, "://")) { 81 | return SRC_URL; 82 | } 83 | 84 | if (pkg_faccessat(p->repo_fd, p->name, src) == 0) { 85 | return SRC_REL; 86 | } 87 | 88 | return -1; 89 | } 90 | 91 | int pkg_sort_name(void const *a, void const *b) { 92 | pkg const *p1 = *(pkg const **) a; 93 | pkg const *p2 = *(pkg const **) b; 94 | 95 | return strcmp(p1->name, p2->name); 96 | } 97 | -------------------------------------------------------------------------------- /src/repo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "arr.h" 9 | #include "buf.h" 10 | #include "error.h" 11 | #include "repo.h" 12 | 13 | struct repo *repo_open(const char *path) { 14 | int n = open(path, O_RDONLY); 15 | 16 | if (n == -1) { 17 | err_no("failed to open repo '%s'", path); 18 | return NULL; 19 | } 20 | 21 | size_t l = strlen(path) + 1; 22 | struct repo *r = malloc(sizeof *r + l); 23 | 24 | if (!r) { 25 | return NULL; 26 | } 27 | 28 | r->fd = n; 29 | memcpy(r->path, path, l); 30 | return r; 31 | } 32 | 33 | struct repo *repo_open_db(const char *type) { 34 | buf *db = buf_alloc(0, 128); 35 | 36 | if (!db) { 37 | return NULL; 38 | } 39 | 40 | buf_push_s(&db, getenv("KISS_ROOT")); 41 | buf_rstrip(&db, '/'); 42 | buf_push_l(&db, "/var/db/kiss/", 13); 43 | buf_push_s(&db, type); 44 | 45 | struct repo *n = repo_open(db); 46 | 47 | buf_free(&db); 48 | 49 | if (!n) { 50 | return NULL; 51 | } 52 | 53 | return n; 54 | } 55 | 56 | int repo_open_db_push(struct repo **r, const char *type) { 57 | struct repo *n = repo_open_db(type); 58 | 59 | if (!n) { 60 | return -1; 61 | } 62 | 63 | arr_push_b(r, n); 64 | return 0; 65 | } 66 | 67 | int repo_open_PATH(struct repo **r, const char *PATH) { 68 | if (!PATH || !PATH[0]) { 69 | goto open_db; 70 | } 71 | 72 | char *p = strdup(PATH); 73 | 74 | if (!p) { 75 | return -ENOMEM; 76 | } 77 | 78 | for (char *t = strtok(p, ":"); t; t = strtok(NULL, ":")) { 79 | struct repo *n = repo_open(t); 80 | 81 | if (!n) { 82 | free(p); 83 | return -1; 84 | } 85 | 86 | arr_push_b(r, n); 87 | } 88 | 89 | free(p); 90 | 91 | open_db:; 92 | struct repo *n = repo_open_db("installed"); 93 | 94 | if (!n) { 95 | return -1; 96 | } 97 | 98 | arr_push_b(r, n); 99 | return 0; 100 | } 101 | 102 | int repo_has_pkg(struct repo *r, const char *pkg) { 103 | return faccessat(r->fd, pkg, F_OK, 0) == 0 ? 1 : 0; 104 | } 105 | 106 | int repo_find_pkg(struct repo **r, const char *pkg) { 107 | for (size_t i = 0; i < arr_len(r); i++) { 108 | if (repo_has_pkg(r[i], pkg)) { 109 | return r[i]->fd; 110 | } 111 | } 112 | 113 | return -1; 114 | } 115 | 116 | void repo_free(struct repo *r) { 117 | close(r->fd); 118 | free(r); 119 | } 120 | 121 | void repo_free_all(struct repo **r) { 122 | for (size_t i = 0; i < arr_len(r); i++) { 123 | repo_free(r[i]); 124 | } 125 | 126 | arr_free(r); 127 | } 128 | 129 | -------------------------------------------------------------------------------- /src/sha256.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "sha256.h" 6 | 7 | void sha256_file(unsigned char hash[SHA256_LEN], FILE *f) { 8 | unsigned char buf[BUFSIZ]; 9 | 10 | SHA256_CTX ctx; 11 | SHA256_Init(&ctx); 12 | 13 | for (size_t n = 0; (n = fread(buf, 1, BUFSIZ, f)); ) { 14 | SHA256_Update(&ctx, buf, n); 15 | } 16 | 17 | SHA256_Final(hash, &ctx); 18 | } 19 | 20 | void sha256_to_string(unsigned char hash[SHA256_LEN], char out[65]) { 21 | for (size_t i = 0; i < SHA256_LEN; i++) { 22 | snprintf(out + (i * 2), 65, "%02x", hash[i]); 23 | } 24 | 25 | out[64] = 0; 26 | } 27 | 28 | /** 29 | * Fallback internal sha256 implementation. 30 | * Modified to match OpenSSL's sha256 functions. 31 | * 32 | * 2010-06-11 : Igor Pavlov : Public domain 33 | * This code is based on public domain code from Wei Dai's Crypto++ library. 34 | */ 35 | #ifndef SHA256_USE_OPENSSL 36 | #ifndef SHA256_USE_BEARSSL 37 | 38 | void SHA256_Init(SHA256_CTX *c) { 39 | c->count = 0; 40 | 41 | c->state[0] = 0x6a09e667; 42 | c->state[1] = 0xbb67ae85; 43 | c->state[2] = 0x3c6ef372; 44 | c->state[3] = 0xa54ff53a; 45 | c->state[4] = 0x510e527f; 46 | c->state[5] = 0x9b05688c; 47 | c->state[6] = 0x1f83d9ab; 48 | c->state[7] = 0x5be0cd19; 49 | } 50 | 51 | #define rotrFixed(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) 52 | #define S0(x) (rotrFixed(x, 2) ^ rotrFixed(x,13) ^ rotrFixed(x, 22)) 53 | #define S1(x) (rotrFixed(x, 6) ^ rotrFixed(x,11) ^ rotrFixed(x, 25)) 54 | #define s0(x) (rotrFixed(x, 7) ^ rotrFixed(x,18) ^ ((x) >> 3)) 55 | #define s1(x) (rotrFixed(x,17) ^ rotrFixed(x,19) ^ ((x) >> 10)) 56 | 57 | #define blk0(i) (W[i] = data[i]) 58 | #define blk2(i) \ 59 | (W[(i)&15] += s1(W[((i)-2)&15]) + W[((i)-7)&15] + s0(W[((i)-15)&15])) 60 | 61 | #define Ch(x,y,z) ((z)^((x)&((y)^(z)))) 62 | #define Maj(x,y,z) (((x)&(y))|((z)&((x)|(y)))) 63 | 64 | #define a(i) T[(0-(i))&7] 65 | #define b(i) T[(1-(i))&7] 66 | #define c(i) T[(2-(i))&7] 67 | #define d(i) T[(3-(i))&7] 68 | #define e(i) T[(4-(i))&7] 69 | #define f(i) T[(5-(i))&7] 70 | #define g(i) T[(6-(i))&7] 71 | #define h(i) T[(7-(i))&7] 72 | 73 | #define R(a,b,c,d,e,f,g,h, i) h += S1(e) + Ch(e,f,g) + K[(i)+(j)] + \ 74 | ((j)?blk2(i):blk0(i)); (d) += (h); (h) += S0(a) + Maj(a, b, c) 75 | 76 | #define RX_8(i) \ 77 | R(a,b,c,d,e,f,g,h, (i)); \ 78 | R(h,a,b,c,d,e,f,g, (i)+1); \ 79 | R(g,h,a,b,c,d,e,f, (i)+2); \ 80 | R(f,g,h,a,b,c,d,e, (i)+3); \ 81 | R(e,f,g,h,a,b,c,d, (i)+4); \ 82 | R(d,e,f,g,h,a,b,c, (i)+5); \ 83 | R(c,d,e,f,g,h,a,b, (i)+6); \ 84 | R(b,c,d,e,f,g,h,a, (i)+7) 85 | 86 | static const uint32_t K[64] = { 87 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 88 | 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 89 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 90 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 91 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 92 | 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 93 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 94 | 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 95 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 96 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 97 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 98 | 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 99 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 100 | 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 101 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 102 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 103 | }; 104 | 105 | static void sha256_transform(uint32_t *state, const uint32_t *data) { 106 | uint32_t W[16] = {0}; 107 | 108 | uint32_t a = state[0]; 109 | uint32_t b = state[1]; 110 | uint32_t c = state[2]; 111 | uint32_t d = state[3]; 112 | uint32_t e = state[4]; 113 | uint32_t f = state[5]; 114 | uint32_t g = state[6]; 115 | uint32_t h = state[7]; 116 | 117 | unsigned j = 0; 118 | RX_8(0); 119 | RX_8(8); 120 | j += 16; 121 | RX_8(0); 122 | RX_8(8); 123 | j += 16; 124 | RX_8(0); 125 | RX_8(8); 126 | j += 16; 127 | RX_8(0); 128 | RX_8(8); 129 | 130 | #undef S0 131 | #undef S1 132 | #undef s0 133 | #undef s1 134 | 135 | state[0] += a; 136 | state[1] += b; 137 | state[2] += c; 138 | state[3] += d; 139 | state[4] += e; 140 | state[5] += f; 141 | state[6] += g; 142 | state[7] += h; 143 | } 144 | 145 | static void sha256_write(SHA256_CTX *c) { 146 | uint32_t data[16]; 147 | 148 | for (unsigned i = 0; i < 16; i++) { 149 | data[i] = 150 | ((uint32_t)(c->buf[i * 4 ]) << 24) + 151 | ((uint32_t)(c->buf[i * 4 + 1]) << 16) + 152 | ((uint32_t)(c->buf[i * 4 + 2]) << 8) + 153 | ((uint32_t)(c->buf[i * 4 + 3])); 154 | } 155 | 156 | sha256_transform(c->state, data); 157 | } 158 | 159 | void SHA256_Update(SHA256_CTX *c, const void *data, size_t size) { 160 | const unsigned char *d = data; 161 | uint32_t pos = c->count & 0x3F; 162 | 163 | while (size > 0) { 164 | c->buf[pos++] = *d++; 165 | c->count++; 166 | size--; 167 | 168 | if (pos == 64) { 169 | pos = 0; 170 | sha256_write(c); 171 | } 172 | } 173 | } 174 | 175 | void SHA256_Final(unsigned char *md, SHA256_CTX *c) { 176 | uint64_t len_bits = (c->count << 3); 177 | uint32_t pos = c->count & 0x3F; 178 | 179 | c->buf[pos++] = 0x80; 180 | 181 | while (pos != (64 - 8)) { 182 | pos &= 0x3F; 183 | 184 | if (pos == 0) { 185 | sha256_write(c); 186 | } 187 | 188 | c->buf[pos++] = 0; 189 | } 190 | 191 | for (unsigned i = 0; i < 8; i++) { 192 | c->buf[pos++] = (len_bits >> 56); 193 | len_bits <<= 8; 194 | } 195 | 196 | sha256_write(c); 197 | 198 | for (unsigned i = 0; i < 8; i++) { 199 | *md++ = (c->state[i] >> 24); 200 | *md++ = (c->state[i] >> 16); 201 | *md++ = (c->state[i] >> 8); 202 | *md++ = (c->state[i]); 203 | } 204 | } 205 | 206 | #endif 207 | #endif 208 | 209 | -------------------------------------------------------------------------------- /src/tar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "arr.h" 8 | #include "buf.h" 9 | #include "error.h" 10 | #include "file.h" 11 | #include "tar.h" 12 | #include "util.h" 13 | 14 | /** 15 | * Use libarchive for tar archives. 16 | */ 17 | #ifdef TAR_USE_LIBARCHIVE 18 | 19 | static const int archive_opts = \ 20 | // Refuse to extract an absolute path. 21 | ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS | 22 | 23 | // Refuse to extract path containing .. 24 | ARCHIVE_EXTRACT_SECURE_NODOTDOT | 25 | 26 | // Refuse to extract any object whose final location would be altered 27 | // by a symlink on disk 28 | ARCHIVE_EXTRACT_SECURE_SYMLINKS 29 | ; 30 | 31 | static int tar_write_data(struct archive *r, struct archive *w) { 32 | const void *buf = 0; 33 | int64_t off = 0; 34 | size_t l = 0; 35 | 36 | for (int ret = 0; ;) { 37 | if ((ret = archive_read_data_block(r, &buf, &l, &off)) == ARCHIVE_EOF) { 38 | break; 39 | } 40 | 41 | if (ret != ARCHIVE_OK) { 42 | err("failed to extract archive: %s", archive_error_string(r)); 43 | return -1; 44 | } 45 | 46 | if ((ret = archive_write_data_block(w, buf, l, off)) != ARCHIVE_OK) { 47 | err("failed to extract archive: %s", archive_error_string(r)); 48 | return -1; 49 | } 50 | } 51 | 52 | return 0; 53 | } 54 | 55 | static int tar_strip_component(struct archive_entry *e) { 56 | const char *path = archive_entry_pathname(e); 57 | 58 | if (!path) { 59 | err("failed to allocate memory"); 60 | return -1; 61 | } 62 | 63 | if (strchr(path, '/')) { 64 | path = strchr(path, '/'); 65 | } 66 | 67 | if (*path == '/') { 68 | path++; 69 | } 70 | 71 | archive_entry_set_pathname(e, path); 72 | return 0; 73 | } 74 | 75 | static int tar_enable_compression(struct archive *a, int compress) { 76 | switch (compress) { 77 | #ifdef ARCHIVE_FILTER_BZIP2 78 | case TAR_BZ2: 79 | return archive_write_add_filter_bzip2(a); 80 | #endif 81 | 82 | #ifdef ARCHIVE_FILTER_GZIP 83 | case TAR_GZ: 84 | return archive_write_add_filter_gzip(a); 85 | #endif 86 | 87 | #ifdef ARCHIVE_FILTER_XZ 88 | case TAR_XZ: 89 | return archive_write_add_filter_xz(a); 90 | #endif 91 | 92 | #ifdef ARCHIVE_FILTER_LZIP 93 | case TAR_LZ: 94 | return archive_write_add_filter_lzip(a); 95 | #endif 96 | 97 | #ifdef ARCHIVE_FILTER_LZMA 98 | case TAR_LZMA: 99 | return archive_write_add_filter_lzma(a); 100 | #endif 101 | 102 | #ifdef ARCHIVE_FILTER_ZSTD 103 | case TAR_ZSTD: 104 | return archive_write_add_filter_zstd(a); 105 | #endif 106 | 107 | case TAR_NONE: 108 | return ARCHIVE_OK; 109 | } 110 | 111 | err("no compression method found"); 112 | err("was libarchive configured correctly?"); 113 | return -1; 114 | } 115 | 116 | static int tar_write_file(struct archive *w, const char *f) { 117 | int ret = 0; 118 | struct archive *r = archive_read_disk_new(); 119 | 120 | if (!r) { 121 | err("failed to allocate memory"); 122 | return -1; 123 | } 124 | 125 | if ((ret = archive_read_disk_set_standard_lookup(r)) != ARCHIVE_OK) { 126 | err("failed to read file: %s", archive_error_string(r)); 127 | goto r_error; 128 | } 129 | 130 | if ((ret = archive_read_disk_open(r, f)) != ARCHIVE_OK) { 131 | err("failed to open file: %s", archive_error_string(r)); 132 | goto r_error; 133 | } 134 | 135 | for (struct archive_entry *e = 0; ;) { 136 | if (!(e = archive_entry_new())) { 137 | err("failed to allocate memory"); 138 | ret = -1; 139 | goto r_error; 140 | } 141 | 142 | ret = archive_read_next_header2(r, e); 143 | 144 | if (ret == ARCHIVE_EOF) { 145 | ret = 0; 146 | goto f_error; 147 | } 148 | 149 | if (ret != ARCHIVE_OK) { 150 | err("failed to read header: %s", archive_error_string(r)); 151 | goto f_error; 152 | } 153 | 154 | archive_read_disk_descend(r); 155 | 156 | if ((ret = archive_write_header(w, e)) != ARCHIVE_OK) { 157 | err("failed to write header: %s", archive_error_string(w)); 158 | goto f_error; 159 | } 160 | 161 | int fd = open(archive_entry_sourcepath(e), O_RDONLY); 162 | 163 | if (fd == -1) { 164 | err_no("failed to open path"); 165 | goto f_error; 166 | } 167 | 168 | char m[4096]; 169 | for (ssize_t l = 0; (l = read(fd, m, sizeof(m))) > 0;) { 170 | if ((ret = archive_write_data(w, m, l)) == -1) { 171 | err("failed to write data: %s", archive_error_string(w)); 172 | goto d_error; 173 | } 174 | } 175 | 176 | close(fd); 177 | archive_entry_free(e); 178 | continue; 179 | 180 | d_error: 181 | close(fd); 182 | 183 | f_error: 184 | archive_entry_free(e); 185 | break; 186 | } 187 | 188 | r_error: 189 | archive_read_close(r); 190 | archive_read_free(r); 191 | return ret; 192 | } 193 | 194 | int tar_create(const char *d, const char *f, int compression) { 195 | if (!f || !*f) { 196 | err("empty input"); 197 | return -1; 198 | } 199 | 200 | struct archive *w = archive_write_new(); 201 | 202 | if (!w) { 203 | err("failed to allocate memory"); 204 | return -1; 205 | } 206 | 207 | int ret = 0; 208 | 209 | if ((ret = tar_enable_compression(w, compression)) != ARCHIVE_OK) { 210 | err("failed to enable compression: %s", archive_error_string(w)); 211 | goto w_error; 212 | } 213 | 214 | if ((ret = archive_write_set_format_ustar(w)) != ARCHIVE_OK) { 215 | err("failed to set format: %s", archive_error_string(w)); 216 | goto w_error; 217 | } 218 | 219 | if ((ret = archive_write_open_filename(w, f)) != ARCHIVE_OK) { 220 | err("failed to create archive: %s", archive_error_string(w)); 221 | goto w_error; 222 | } 223 | 224 | DIR *dir = opendir(d); 225 | 226 | if (!dir) { 227 | err_no("failed to open directory '%s'", d); 228 | ret = -1; 229 | goto w_error; 230 | } 231 | 232 | buf *mem = buf_alloc(0, 256); 233 | 234 | if (!mem) { 235 | err("failed to allocate memory"); 236 | ret = -1; 237 | goto w_error; 238 | } 239 | 240 | if ((ret = buf_push_s(&mem, d)) < 0) { 241 | err_no("buf error"); 242 | goto b_error; 243 | } 244 | 245 | buf_rstrip(&mem, '/'); 246 | buf_push_c(&mem, '/'); 247 | buf_push_c(&mem, 0); 248 | 249 | size_t len_pre = buf_len(mem); 250 | 251 | for (struct dirent *dp = 0; (dp = read_dir(dir)); ) { 252 | if ((ret = buf_push_s(&mem, dp->d_name)) < 0) { 253 | err_no("buf erorr"); 254 | break; 255 | } 256 | 257 | if ((ret = tar_write_file(w, mem)) != 0) { 258 | err("failed to read dir: %s", archive_error_string(w)); 259 | break; 260 | } 261 | 262 | buf_set_len(&mem, len_pre); 263 | } 264 | 265 | b_error: 266 | buf_free(&mem); 267 | closedir(dir); 268 | 269 | w_error: 270 | archive_write_close(w); 271 | archive_write_free(w); 272 | 273 | return ret; 274 | } 275 | 276 | int tar_extract(const char *f) { 277 | if (!f || !*f) { 278 | err("empty input"); 279 | return -1; 280 | } 281 | 282 | struct archive *r = archive_read_new(); 283 | 284 | if (!r) { 285 | return -1; 286 | } 287 | 288 | int ret = 0; 289 | struct archive *w = archive_write_disk_new(); 290 | 291 | if (!w) { 292 | ret = -1; 293 | goto r_error; 294 | } 295 | 296 | archive_write_disk_set_options(w, archive_opts); 297 | archive_read_support_format_all(r); 298 | archive_read_support_filter_all(r); 299 | 300 | if ((ret = archive_write_disk_set_standard_lookup(w)) != ARCHIVE_OK) { 301 | err("failed to extract archive: %s", archive_error_string(w)); 302 | goto w_error; 303 | } 304 | 305 | if ((ret = archive_read_open_filename(r, f, 10240)) != ARCHIVE_OK) { 306 | err("failed to open archive: %s", archive_error_string(r)); 307 | goto w_error; 308 | } 309 | 310 | for (struct archive_entry *e = 0; ;) { 311 | if ((ret = archive_read_next_header(r, &e)) == ARCHIVE_EOF) { 312 | ret = 0; 313 | break; 314 | } 315 | 316 | if (ret != ARCHIVE_OK) { 317 | err("failed to extract archive: %s", archive_error_string(r)); 318 | break; 319 | } 320 | 321 | if ((ret = tar_strip_component(e)) < 0) { 322 | break; 323 | } 324 | 325 | archive_write_header(w, e); 326 | 327 | if ((ret = tar_write_data(r, w)) < 0) { 328 | break; 329 | } 330 | } 331 | 332 | w_error: 333 | archive_write_close(w); 334 | archive_write_free(w); 335 | r_error: 336 | archive_read_close(r); 337 | archive_read_free(r); 338 | 339 | return ret; 340 | } 341 | 342 | /** 343 | * Fallback to executing tar utility if libarchive not available. 344 | * 345 | * Requires a "smart" tar that is aware of compression and can automagically 346 | * deal with it (GNU tar, libarchive tar (BSDtar), busybox tar, ...). 347 | */ 348 | #else 349 | 350 | int tar_create(const char *d, const char *f, int compression) { 351 | (void) compression; 352 | 353 | if (!d || !*d || !f || !*f) { 354 | return -1; 355 | } 356 | 357 | const char *cmd[] = { 358 | "tar", "acf", f, d, 0 359 | }; 360 | 361 | return run_cmd((char **) cmd); 362 | } 363 | 364 | int tar_extract(const char *f) { 365 | if (!f || !*f) { 366 | return -1; 367 | } 368 | 369 | const char *cmd[] = { 370 | "tar", "xf", f, "--strip-components", "1", 0 371 | }; 372 | 373 | return run_cmd((char **) cmd); 374 | } 375 | 376 | #endif 377 | 378 | -------------------------------------------------------------------------------- /src/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "error.h" 5 | #include "test.h" 6 | 7 | static int test_count; 8 | static int test_error_count; 9 | 10 | int test_begin(const char *file) { 11 | msg("Running tests for %s:", file); 12 | 13 | test_count = 0; 14 | test_error_count = 0; 15 | 16 | return 0; 17 | } 18 | 19 | void test_internal(const char *expr, int result, int line) { 20 | msg("[line %03d] %s %s", line, result ? "PASS" : "FAIL", expr); 21 | 22 | test_error_count += result ? 0 : 1; 23 | test_count++; 24 | } 25 | 26 | int test_finish(void) { 27 | msg("Ran %d tests, %d succeeded, %d failed", 28 | test_count, test_count - test_error_count, test_error_count); 29 | 30 | return test_error_count ? 1 : 0; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "error.h" 6 | #include "util.h" 7 | 8 | char *human_readable(uint64_t n, char out[6]) { 9 | out[5] = '\0'; 10 | 11 | if (n <= 0x400) { 12 | out[4] = 'B'; 13 | 14 | } else if (n <= 0x100000) { 15 | out[4] = 'K'; 16 | n >>= 10; 17 | 18 | // XX.XM for < 100MB~ 19 | } else if (n < 100000000) { 20 | out[4] = 'M'; 21 | out[3] = '0' + ((n /= 0x19000) % 10); 22 | out[2] = '.'; 23 | goto fake; 24 | 25 | } else if (n <= 0x40000000) { 26 | out[4] = 'M'; 27 | n >>= 20; 28 | 29 | } else if (n <= 0x10000000000) { 30 | out[4] = 'G'; 31 | n >>= 30; 32 | 33 | } else if (n <= 0x4000000000000) { 34 | out[4] = 'T'; 35 | n >>= 40; 36 | 37 | } else { 38 | out[4] = 'P'; 39 | n >>= 50; 40 | } 41 | 42 | out[3] = '0' + (n % 10); 43 | out[2] = '0' + ((n /= 10) % 10); 44 | fake: 45 | out[1] = '0' + ((n /= 10) % 10); 46 | out[0] = '0' + ((n / 10) % 10); 47 | 48 | return out; 49 | } 50 | 51 | int run_cmd(char *const argv[]) { 52 | pid_t pid = fork(); 53 | 54 | if (pid == -1) { 55 | err_no("failed to fork"); 56 | return -1; 57 | } 58 | 59 | if (pid == 0) { 60 | execvp(argv[0], argv); 61 | 62 | } else { 63 | int status = 0; 64 | 65 | waitpid(pid, &status, 0); 66 | 67 | if (WEXITSTATUS(status)) { 68 | err_no("command exited %d", status); 69 | return -1; 70 | } 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | int qsort_cb_str(void const *a, void const *b) { 77 | char const *p1 = *(char const **) a; 78 | char const *p2 = *(char const **) b; 79 | 80 | return strcmp(p1, p2); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /test/test-action.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "action.h" 7 | #include "test.h" 8 | 9 | static const char *pkgs[] = { 10 | "", 11 | "", 12 | "zlib", 13 | "samurai" 14 | }; 15 | 16 | static char *path_basename(char *p) { 17 | for (size_t i = strlen(p); p[--i] == '/'; p[i] = 0); 18 | 19 | char *b = strrchr(p, '/'); 20 | 21 | // p now points to dirname 22 | return p ? b ? (*b++ = 0, b) : (*p = '/', p) : NULL; 23 | } 24 | 25 | int main(int argc, char *argv[]) { 26 | (void) argc; 27 | (void) argv; 28 | 29 | int err = test_begin(__FILE__); 30 | struct state *s = 0; 31 | 32 | s = state_init(4, (char **) pkgs, STATE_ALL); { 33 | test(s); 34 | test(s->opt == STATE_ALL); 35 | test(s->mem); 36 | test(buf_len(s->mem) == 0); 37 | test(buf_cap(s->mem) == 1024); 38 | test(strcmp(s->pkgs[0]->name, "zlib") == 0); 39 | test(strcmp(s->pkgs[1]->name, "samurai") == 0); 40 | test(s->repos); 41 | test(s->repos[0]->path[0]); 42 | 43 | for (size_t i = 0; i < CAC_DIR; i++) { 44 | test(fcntl(s->cache.fd[i], F_GETFL) != -1 || errno != EBADF); 45 | } 46 | } 47 | state_free(s); 48 | 49 | s = state_init(0, NULL, STATE_MEM); { 50 | test(s); 51 | test(s->opt == STATE_MEM); 52 | test(s->mem); 53 | test(buf_len(s->mem) == 0); 54 | test(buf_cap(s->mem) == 1024); 55 | test(!s->pkgs); 56 | test(!s->repos); 57 | test(!s->cache.dir); 58 | } 59 | state_free(s); 60 | 61 | s = state_init(0, NULL, STATE_REPO | STATE_KISS_PATH); { 62 | test(s); 63 | test(s->opt == (STATE_REPO | STATE_KISS_PATH)); 64 | test(!s->mem); 65 | test(!s->pkgs); 66 | test(s->repos); 67 | test(s->repos[0]->path[0]); 68 | test(!s->cache.dir); 69 | } 70 | state_free(s); 71 | 72 | s = state_init(0, NULL, STATE_CACHE); { 73 | test(s); 74 | test(s->opt == STATE_CACHE); 75 | test(!s->mem); 76 | test(!s->pkgs); 77 | test(!s->repos); 78 | test(s->cache.dir); 79 | 80 | for (size_t i = 0; i < CAC_DIR; i++) { 81 | test(fcntl(s->cache.fd[i], F_GETFL) != -1 || errno != EBADF); 82 | } 83 | } 84 | state_free(s); 85 | 86 | s = state_init(4, (char **) pkgs, STATE_PKG); { 87 | test(s); 88 | test(s->opt == STATE_PKG); 89 | test(!s->mem); 90 | test(s->pkgs); 91 | test(!s->repos); 92 | test(!s->cache.dir); 93 | test(strcmp(s->pkgs[0]->name, "zlib") == 0); 94 | test(strcmp(s->pkgs[1]->name, "samurai") == 0); 95 | test(s->pkgs[0]->repo_fd == 0); 96 | test(s->pkgs[1]->repo_fd == 0); 97 | } 98 | state_free(s); 99 | 100 | s = state_init(4, (char **) pkgs, STATE_PKG | STATE_CACHE); { 101 | test(s); 102 | test(s->opt == (STATE_PKG | STATE_CACHE)); 103 | test(!s->mem); 104 | test(s->pkgs); 105 | test(!s->repos); 106 | test(s->cache.dir); 107 | test(strcmp(s->pkgs[0]->name, "zlib") == 0); 108 | test(strcmp(s->pkgs[1]->name, "samurai") == 0); 109 | test(s->pkgs[0]->repo_fd == 0); 110 | test(s->pkgs[1]->repo_fd == 0); 111 | } 112 | state_free(s); 113 | 114 | s = state_init(4, (char **) pkgs, 115 | STATE_PKG | STATE_CACHE | STATE_PKG_CACHE); { 116 | test(s); 117 | test(s->opt == (STATE_PKG | STATE_CACHE | STATE_PKG_CACHE)); 118 | test(!s->mem); 119 | test(s->pkgs); 120 | test(!s->repos); 121 | test(s->cache.dir); 122 | test(strcmp(s->pkgs[0]->name, "zlib") == 0); 123 | test(strcmp(s->pkgs[1]->name, "samurai") == 0); 124 | } 125 | state_free(s); 126 | 127 | s = state_init(4, (char **) pkgs, STATE_SEARCH); { 128 | test(s); 129 | test(s->opt == STATE_SEARCH); 130 | test(s->mem); 131 | test(s->pkgs); 132 | test(s->repos); 133 | test(!s->cache.dir); 134 | test(strcmp(s->pkgs[0]->name, "zlib") == 0); 135 | test(strcmp(s->pkgs[1]->name, "samurai") == 0); 136 | test(fcntl(s->pkgs[0]->repo_fd, F_GETFL) != -1 || errno != EBADF); 137 | test(fcntl(s->pkgs[1]->repo_fd, F_GETFL) != -1 || errno != EBADF); 138 | } 139 | state_free(s); 140 | 141 | s = state_init(2, NULL, STATE_PKG | STATE_PKG_PWD | STATE_MEM); { 142 | test(s); 143 | test(s->opt == (STATE_PKG | STATE_PKG_PWD | STATE_MEM)); 144 | test(s->mem); 145 | test(s->pkgs); 146 | test(!s->repos); 147 | test(!s->cache.dir); 148 | test(strcmp(s->pkgs[0]->name, strrchr(getenv("PWD"), '/') + 1) == 0); 149 | 150 | char *pwd = strdup(getenv("PWD")); 151 | path_basename(pwd); 152 | test(strncmp(getenv("KISS_PATH"), pwd, strlen(pwd)) == 0); 153 | free(pwd); 154 | } 155 | state_free(s); 156 | 157 | s = state_init(4, (char **) pkgs, STATE_PKG | STATE_MEM); { 158 | test(s); 159 | test(s->opt == (STATE_PKG | STATE_MEM)); 160 | test(s->mem); 161 | test(s->pkgs); 162 | test(!s->repos); 163 | test(!s->cache.dir); 164 | 165 | err = action_list(s); { 166 | test(err == 0); 167 | } 168 | } 169 | state_free(s); 170 | 171 | s = state_init(2, NULL, STATE_PKG | STATE_MEM); { 172 | test(s); 173 | test(s->opt == (STATE_PKG | STATE_MEM)); 174 | test(s->mem); 175 | test(s->pkgs); 176 | test(!s->repos); 177 | test(!s->cache.dir); 178 | 179 | err = action_list(s); { 180 | test(err == 0); 181 | } 182 | } 183 | state_free(s); 184 | 185 | s = state_init(4, (char **) pkgs, STATE_SEARCH); { 186 | test(s); 187 | test(s->opt == STATE_SEARCH); 188 | test(s->mem); 189 | test(s->pkgs); 190 | test(s->repos); 191 | test(!s->cache.dir); 192 | 193 | err = action_search(s); { 194 | test(err == 0); 195 | } 196 | } 197 | state_free(s); 198 | 199 | s = state_init(4, (char **) pkgs, STATE_ALL); { 200 | test(s); 201 | test(s->opt == STATE_ALL); 202 | test(s->mem); 203 | test(buf_len(s->mem) == 0); 204 | test(buf_cap(s->mem) == 1024); 205 | test(strcmp(s->pkgs[0]->name, "zlib") == 0); 206 | test(strcmp(s->pkgs[1]->name, "samurai") == 0); 207 | test(s->repos); 208 | test(s->repos[0]->path[0]); 209 | 210 | for (size_t i = 0; i < CAC_DIR; i++) { 211 | test(fcntl(s->cache.fd[i], F_GETFL) != -1 || errno != EBADF); 212 | } 213 | 214 | err = action_checksum(s); { 215 | test(err == 0); 216 | } 217 | } 218 | state_free(s); 219 | 220 | return test_finish(); 221 | } 222 | 223 | -------------------------------------------------------------------------------- /test/test-arr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "arr.h" 5 | #include "test.h" 6 | #include "util.h" 7 | 8 | static const char *fruit[] = { 9 | "apple", 10 | "orange", 11 | "pear", 12 | "banana", 13 | "peach", 14 | "lime" 15 | }; 16 | 17 | int main(int argc, char *argv[]) { 18 | (void) argc; 19 | (void) argv; 20 | 21 | test_begin(__FILE__); 22 | 23 | char **list = arr_alloc(0, 4); { 24 | test(list); 25 | test(arr_cap(list) == 4); 26 | test(arr_len(list) == 0); 27 | } 28 | 29 | for (size_t i = 0; i < sizeof(fruit) / sizeof(fruit[0]); i++) { 30 | char *s = strdup(fruit[i]); 31 | 32 | arr_push_b(list, s); { 33 | test(arr_len(list) == i + 1); 34 | 35 | for (size_t i = 0; i < arr_len(list); i++) { 36 | test(strcmp(list[i], fruit[i]) == 0); 37 | } 38 | } 39 | } 40 | 41 | arr_sort(list, qsort_cb_str); { 42 | test(arr_cap(list) == 6); 43 | test(arr_len(list) == 6); 44 | test(strcmp(list[0], "apple") == 0); 45 | test(strcmp(list[1], "banana") == 0); 46 | test(strcmp(list[2], "lime") == 0); 47 | test(strcmp(list[3], "orange") == 0); 48 | test(strcmp(list[4], "peach") == 0); 49 | test(strcmp(list[5], "pear") == 0); 50 | } 51 | 52 | free(list[arr_len(list) - 1]); 53 | arr_drop_b(list); { 54 | test(arr_cap(list) == 6); 55 | test(arr_len(list) == 5); 56 | test(strcmp(list[0], "apple") == 0); 57 | test(strcmp(list[1], "banana") == 0); 58 | test(strcmp(list[2], "lime") == 0); 59 | test(strcmp(list[3], "orange") == 0); 60 | test(strcmp(list[4], "peach") == 0); 61 | } 62 | 63 | free(list[arr_len(list) - 1]); 64 | arr_drop_b(list); { 65 | test(arr_cap(list) == 6); 66 | test(arr_len(list) == 4); 67 | test(strcmp(list[0], "apple") == 0); 68 | test(strcmp(list[1], "banana") == 0); 69 | test(strcmp(list[2], "lime") == 0); 70 | test(strcmp(list[3], "orange") == 0); 71 | } 72 | 73 | for (size_t i = 0; i < arr_len(list); i++) { 74 | free(list[i]); 75 | } 76 | arr_free(list); 77 | 78 | return test_finish(); 79 | } 80 | 81 | -------------------------------------------------------------------------------- /test/test-buf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "buf.h" 5 | #include "test.h" 6 | 7 | int main(int argc, char *argv[]) { 8 | (void) argc; 9 | (void) argv; 10 | 11 | int ret = test_begin(__FILE__); 12 | 13 | buf *s = buf_alloc(0, 0); { 14 | test(s); 15 | test(buf_len(s) == 0); 16 | test(buf_cap(s) == 0); 17 | } 18 | 19 | ret = buf_push_s(&s, "hello, world"); { 20 | test(ret == 0); 21 | test(buf_len(s) == 12); 22 | test(buf_cap(s) == 18); 23 | test(strcmp(s, "hello, world") == 0); 24 | } 25 | 26 | buf_set_len(s, 0); { 27 | test(buf_len(s) == 0); 28 | test(buf_cap(s) == 18); 29 | test(s[0] == 0); 30 | test(strcmp(s, "") == 0); 31 | } 32 | 33 | ret = buf_push_l(&s, "123456", 6); { 34 | test(ret == 0); 35 | test(buf_len(s) == 6); 36 | test(buf_cap(s) == 18); 37 | test(strcmp(s, "123456") == 0); 38 | } 39 | 40 | ret = buf_push_c(&s, '!'); { 41 | test(ret == 0); 42 | test(buf_len(s) == 7); 43 | test(buf_cap(s) == 18); 44 | test(strcmp(s, "123456!") == 0); 45 | } 46 | 47 | buf_rstrip(&s, '!'); { 48 | test(buf_len(s) == 6); 49 | test(buf_cap(s) == 18); 50 | test(strcmp(s, "123456") == 0); 51 | } 52 | 53 | ret = buf_undo_c(&s, '6'); { 54 | test(ret == 0); 55 | test(buf_len(s) == 5); 56 | test(buf_cap(s) == 18); 57 | test(strcmp(s, "12345") == 0); 58 | } 59 | 60 | ret = buf_undo_c(&s, '6'); { 61 | test(ret == -1); 62 | test(buf_len(s) == 5); 63 | test(buf_cap(s) == 18); 64 | test(strcmp(s, "12345") == 0); 65 | } 66 | 67 | ret = buf_printf(&s, "%d", 2211); { 68 | test(ret == 0); 69 | test(buf_len(s) == 9); 70 | test(buf_cap(s) == 18); 71 | test(strcmp(s, "123452211") == 0); 72 | } 73 | 74 | FILE *f = fopen("test/test_hier/repo/core/gzip/version", "r"); 75 | 76 | ret = buf_getline(&s, f, 30); { 77 | test(ret == 0); 78 | test(buf_len(s) == 15); 79 | test(buf_cap(s) == 63); 80 | test(strcmp(s, "1234522111.10 4") == 0); 81 | } 82 | 83 | ret = buf_getline(&s, f, 30); { 84 | test(ret == -1); 85 | test(buf_len(s) == 15); 86 | test(buf_cap(s) == 63); 87 | test(strcmp(s, "1234522111.10 4") == 0); 88 | } 89 | 90 | fclose(f); 91 | 92 | char *test_buf = 0; 93 | ret = buf_push_s(&s, test_buf); { 94 | test(ret == -EINVAL); 95 | test(buf_len(s) == 15); 96 | test(buf_cap(s) == 63); 97 | test(strcmp(s, "1234522111.10 4") == 0); 98 | } 99 | 100 | ret = buf_set(&s, ' ', 10); { 101 | test(ret == 0); 102 | test(buf_len(s) == 25); 103 | test(buf_cap(s) == 63); 104 | test(strcmp(s, "1234522111.10 4 ") == 0); 105 | } 106 | 107 | ret = buf_scan(&s, 0, ' '); { 108 | test(ret == 14); 109 | test(buf_len(s) == 25); 110 | test(buf_cap(s) == 63); 111 | test(strcmp(s, "1234522111.10") == 0); 112 | test(strcmp(s + 14, "4 ") == 0); 113 | } 114 | 115 | buf_free(&s); 116 | 117 | return test_finish(); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /test/test-cache.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cache.h" 9 | #include "test.h" 10 | 11 | static void cache_check(int err, const char *path) { 12 | struct cache c; 13 | 14 | err = cache_init(&c); { 15 | test(err == 0); 16 | test(c.dir); 17 | test(c.fd[CAC_DIR]); 18 | test(fcntl(c.fd[CAC_DIR], F_GETFL) != -1 || errno != EBADF); 19 | 20 | for (size_t j = 0; j < CAC_DIR; j++) { 21 | test(fcntl(c.fd[j], F_GETFL) != -1 || errno != EBADF); 22 | } 23 | 24 | test(access(c.dir, F_OK) == 0); 25 | test(access(path, F_OK) == 0); 26 | 27 | size_t len_pre = buf_len(c.dir); 28 | buf_printf(&c.dir, "%s/%ld", path, getpid()); 29 | int fd = open(c.dir + len_pre, O_RDONLY); 30 | buf_set_len(c.dir, len_pre); 31 | 32 | test(fcntl(fd, F_GETFL) != -1 || errno != EBADF); 33 | test(faccessat(fd, "build", F_OK, 0) == 0); 34 | test(faccessat(fd, "extract", F_OK, 0) == 0); 35 | test(faccessat(fd, "pkg", F_OK, 0) == 0); 36 | 37 | close(fd); 38 | } 39 | 40 | cache_clean(&c); { 41 | test(access(c.dir, F_OK) == -1); 42 | } 43 | 44 | cache_free(&c); { 45 | for (size_t j = 0; j < CAC_DIR; j++) { 46 | test(fcntl(c.fd[j], F_GETFL) == -1 && errno == EBADF); 47 | } 48 | } 49 | } 50 | 51 | int main(int argc, char *argv[]) { 52 | (void) argc; 53 | (void) argv; 54 | 55 | int err = test_begin(__FILE__); 56 | 57 | // check KISS_TMPDIR 58 | setenv("KISS_TMPDIR", "test/test_hier", 1); 59 | setenv("XDG_CACHE_HOME", "", 1); 60 | setenv("HOME", "", 1); 61 | cache_check(err, "test/test_hier/kiss/proc"); 62 | 63 | // check XDG_CACHE_HOME 64 | setenv("KISS_TMPDIR", "", 1); 65 | setenv("XDG_CACHE_HOME", "test/test_hier", 1); 66 | setenv("HOME", "", 1); 67 | cache_check(err, "test/test_hier/kiss/proc"); 68 | 69 | // check HOME 70 | setenv("KISS_TMPDIR", "", 1); 71 | setenv("XDG_CACHE_HOME", "", 1); 72 | setenv("HOME", "test/test_hier", 1); 73 | cache_check(err, "test/test_hier/.cache/kiss/proc"); 74 | 75 | // check invalid 76 | setenv("KISS_TMPDIR", "", 1); 77 | setenv("XDG_CACHE_HOME", "", 1); 78 | setenv("HOME", "", 1); 79 | struct cache c; 80 | err = cache_init(&c); { 81 | test(err == -1); 82 | } 83 | buf_free(&c.dir); 84 | 85 | return test_finish(); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /test/test-download.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "download.h" 7 | #include "sha256.h" 8 | #include "test.h" 9 | 10 | static const unsigned char file_hash[SHA256_LEN] = { 11 | 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 12 | 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 13 | 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, 14 | }; 15 | 16 | int main(int argc, char *argv[]) { 17 | (void) argc; 18 | (void) argv; 19 | 20 | #ifdef DL_USE_CURL 21 | int ret = test_begin(__FILE__); 22 | 23 | FILE *dest = fopen("test/team.txt", "w"); 24 | 25 | // TODO: Upload file to website specifically for this purpose. 26 | ret = source_download("https://k1ss.org/team.txt", dest); { 27 | test(ret == 0); 28 | test(access("test/team.txt", F_OK) == 0); 29 | } 30 | 31 | unsigned char hash[SHA256_LEN]; 32 | sha256_file(hash, dest); 33 | 34 | for (size_t i = 0; i < SHA256_LEN; i++) { 35 | test(hash[i] == file_hash[i]); 36 | } 37 | 38 | remove("test/team.txt"); 39 | fclose(dest); 40 | 41 | source_curl_cleanup(); 42 | 43 | return test_finish(); 44 | #endif 45 | 46 | return 0; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /test/test-sha256.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "sha256.h" 7 | #include "test.h" 8 | 9 | static const unsigned char test1[SHA256_LEN] = { 10 | 0x77, 0xd1, 0x02, 0x4b, 0xf4, 0xf9, 0x7c, 0x32, 0xf8, 0x15, 0xda, 11 | 0x89, 0x6f, 0x7a, 0xbe, 0xec, 0xeb, 0x49, 0x21, 0x54, 0x89, 0x15, 12 | 0x58, 0xf3, 0x3e, 0xde, 0x29, 0x95, 0x8b, 0xb5, 0x2b, 0x0f, 13 | }; 14 | 15 | static const char *test_sum = \ 16 | "77d1024bf4f97c32f815da896f7abeeceb492154891558f33ede29958bb52b0f"; 17 | 18 | int main(int argc, char *argv[]) { 19 | (void) argc; 20 | (void) argv; 21 | 22 | test_begin(__FILE__); 23 | 24 | unsigned char hash[SHA256_LEN]; 25 | FILE *f = fopen("test/test_hier/repo/core/zlib/version", "r"); 26 | sha256_file(hash, f); 27 | fclose(f); 28 | 29 | for (size_t i = 0; i < SHA256_LEN; i++) { 30 | test(hash[i] == test1[i]); 31 | } 32 | 33 | char hash_string[65]; 34 | sha256_to_string(hash, hash_string); 35 | test(strcmp(hash_string, test_sum) == 0); 36 | 37 | return test_finish(); 38 | } 39 | 40 | -------------------------------------------------------------------------------- /test/test-tar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "file.h" 7 | #include "tar.h" 8 | #include "test.h" 9 | 10 | int main(int argc, char *argv[]) { 11 | (void) argc; 12 | (void) argv; 13 | 14 | int ret = test_begin(__FILE__); 15 | 16 | ret = tar_create("test/test_hier/bin", "test.tar", TAR_NONE); { 17 | test(ret == 0); 18 | test(access("test.tar", F_OK) == 0); 19 | 20 | ret = tar_extract("test.tar"); { 21 | test(ret == 0); 22 | test(access("test_hier/bin/kiss-test", X_OK) == 0); 23 | rm_rf("test_hier"); 24 | test(access("test_hier", F_OK) == -1 && errno == ENOENT); 25 | remove("test.tar"); 26 | } 27 | } 28 | 29 | ret = tar_create("test/test_hier/bin", "test.tar.xz", TAR_XZ); { 30 | test(ret == 0); 31 | test(access("test.tar.xz", F_OK) == 0); 32 | 33 | ret = tar_extract("test.tar.xz"); { 34 | test(ret == 0); 35 | test(access("test_hier/bin/kiss-test", X_OK) == 0); 36 | rm_rf("test_hier"); 37 | test(access("test_hier", F_OK) == -1 && errno == ENOENT); 38 | remove("test.tar.xz"); 39 | } 40 | } 41 | 42 | ret = tar_create("test/test_hier/bin", "test.tar.gz", TAR_GZ); { 43 | test(ret == 0); 44 | test(access("test.tar.gz", F_OK) == 0); 45 | 46 | ret = tar_extract("test.tar.gz"); { 47 | test(ret == 0); 48 | test(access("test_hier/bin/kiss-test", X_OK) == 0); 49 | rm_rf("test_hier"); 50 | test(access("test_hier", F_OK) == -1 && errno == ENOENT); 51 | remove("test.tar.gz"); 52 | } 53 | } 54 | 55 | ret = tar_create("test/test_hier/bin", "test.tar.bz2", TAR_BZ2); { 56 | test(ret == 0); 57 | test(access("test.tar.bz2", F_OK) == 0); 58 | 59 | ret = tar_extract("test.tar.bz2"); { 60 | test(ret == 0); 61 | test(access("test_hier/bin/kiss-test", X_OK) == 0); 62 | rm_rf("test_hier"); 63 | test(access("test_hier", F_OK) == -1 && errno == ENOENT); 64 | remove("test.tar.bz2"); 65 | } 66 | } 67 | 68 | return test_finish(); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /test/test-util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "util.h" 5 | #include "test.h" 6 | 7 | static const uint64_t n[] = { 8 | 0, 9 | 1024, 10 | 1048576, 11 | 1073741824, 12 | 1099511627776, 13 | 1125899906842624, 14 | 1152921504606846976, 15 | 16 | // random integers 17 | 513524396, 18 | 9345333333333305, 19 | 857361879, 20 | 598228394, 21 | 28643413, 22 | 869382, 23 | 493851206, 24 | 1712, 25 | 33522447705, 26 | 2157719, 27 | 245592222222222742, 28 | 80174019, 29 | 777851, 30 | 830733314299, 31 | 165918401, 32 | 2695816, 33 | 428830177, 34 | 973079073, 35 | 5274033345, 36 | 841378620, 37 | 7143039, 38 | 73, 39 | 853, 40 | 80389, 41 | 42229004, 42 | 7947977, 43 | 901873, 44 | 244, 45 | 528543314, 46 | 5517216, 47 | 16304, 48 | 53169001, 49 | 180000000, 50 | 104143736, 51 | 1073741823, 52 | }; 53 | 54 | static const char a[][6] = { 55 | "0000B", 56 | "1024B", 57 | "1024K", 58 | "1024M", 59 | "1024G", 60 | "1024T", 61 | "1024P", 62 | 63 | // random integers 64 | "0489M", 65 | "0008P", 66 | "0817M", 67 | "0570M", 68 | "27.9M", 69 | "0849K", 70 | "0470M", 71 | "0001K", 72 | "0031G", 73 | "02.1M", 74 | "0218P", 75 | "78.2M", 76 | "0759K", 77 | "0773G", 78 | "0158M", 79 | "02.6M", 80 | "0408M", 81 | "0928M", 82 | "0004G", 83 | "0802M", 84 | "06.9M", 85 | "0073B", 86 | "0853B", 87 | "0078K", 88 | "41.2M", 89 | "07.7M", 90 | "0880K", 91 | "0244B", 92 | "0504M", 93 | "05.3M", 94 | "0015K", 95 | "51.9M", 96 | "0171M", 97 | "0099M", 98 | "1023M", 99 | }; 100 | 101 | int main(int argc, char *argv[]) { 102 | (void) argc; 103 | (void) argv; 104 | 105 | test_begin(__FILE__); 106 | 107 | for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); i++) { 108 | test(strcmp(human_readable(n[i], (char [6]){0}), a[i]) == 0); 109 | } 110 | 111 | return test_finish(); 112 | } 113 | 114 | -------------------------------------------------------------------------------- /test/test_hier/bin/kiss-test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | printf 'hello\n' 4 | -------------------------------------------------------------------------------- /test/test_hier/repo/core/gzip/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | make CC="${CC:-cc} -static" 4 | 5 | install -Dm755 pigz "$1/usr/bin/pigz" 6 | install -Dm755 unpigz "$1/usr/bin/unpigz" 7 | install -Dm644 pigz.1 "$1/usr/share/man/man1/pigz.1" 8 | 9 | ln -sf pigz "$1/usr/bin/gzip" 10 | ln -sf pigz "$1/usr/bin/zcat" 11 | ln -sf unpigz "$1/usr/bin/gunzip" 12 | ln -sf pigz.1 "$1/usr/share/man/man1/gzip.1" 13 | -------------------------------------------------------------------------------- /test/test_hier/repo/core/gzip/checksums: -------------------------------------------------------------------------------- 1 | a4f816222a7b4269bd232680590b579ccc72591f1bb5adafcd7208ca77e14f73 pigz-2.4.tar.gz 2 | -------------------------------------------------------------------------------- /test/test_hier/repo/core/gzip/sources: -------------------------------------------------------------------------------- 1 | https://zlib.net/pigz/pigz-2.4.tar.gz 2 | -------------------------------------------------------------------------------- /test/test_hier/repo/core/gzip/version: -------------------------------------------------------------------------------- 1 | 1.10 4 2 | -------------------------------------------------------------------------------- /test/test_hier/repo/core/zlib/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | ./configure \ 4 | --prefix=/usr \ 5 | --libdir=/usr/lib \ 6 | --shared 7 | 8 | make 9 | make DESTDIR="$1" install 10 | -------------------------------------------------------------------------------- /test/test_hier/repo/core/zlib/checksums: -------------------------------------------------------------------------------- 1 | c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 2 | -------------------------------------------------------------------------------- /test/test_hier/repo/core/zlib/sources: -------------------------------------------------------------------------------- 1 | https://zlib.net/zlib-1.2.11.tar.gz 2 | -------------------------------------------------------------------------------- /test/test_hier/repo/core/zlib/version: -------------------------------------------------------------------------------- 1 | 1.2.11 3 2 | -------------------------------------------------------------------------------- /test/test_hier/repo/extra/samurai/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | make PREFIX=/usr 4 | make PREFIX=/usr DESTDIR="$1" install 5 | 6 | ln -s samu "$1/usr/bin/ninja" 7 | -------------------------------------------------------------------------------- /test/test_hier/repo/extra/samurai/checksums: -------------------------------------------------------------------------------- 1 | cb3ce624f26eb6f0ec0118a02b8f5f7953c3b644e229f50043698fc458f2c98e 2 | -------------------------------------------------------------------------------- /test/test_hier/repo/extra/samurai/sources: -------------------------------------------------------------------------------- 1 | https://github.com/michaelforney/samurai/releases/download/1.1/samurai-1.1.tar.gz 2 | -------------------------------------------------------------------------------- /test/test_hier/repo/extra/samurai/version: -------------------------------------------------------------------------------- 1 | 1.1 1 2 | -------------------------------------------------------------------------------- /test/test_hier/var/db/kiss/installed/gzip: -------------------------------------------------------------------------------- 1 | ../../../../repo/core/gzip/ -------------------------------------------------------------------------------- /test/test_hier/var/db/kiss/installed/samurai: -------------------------------------------------------------------------------- 1 | ../../../../repo/extra/samurai/ -------------------------------------------------------------------------------- /test/test_hier/var/db/kiss/installed/zlib: -------------------------------------------------------------------------------- 1 | ../../../../repo/core/zlib/ -------------------------------------------------------------------------------- /test/valgrind/lzma.supp: -------------------------------------------------------------------------------- 1 | { 2 | libarchive-lzma-glibc-uninitialized-values 3 | Memcheck:Cond 4 | obj:/lib/x86_64-linux-gnu/liblzma.so.5.2.2 5 | obj:/lib/x86_64-linux-gnu/liblzma.so.5.2.2 6 | obj:/lib/x86_64-linux-gnu/liblzma.so.5.2.2 7 | obj:/lib/x86_64-linux-gnu/liblzma.so.5.2.2 8 | obj:/lib/x86_64-linux-gnu/liblzma.so.5.2.2 9 | fun:lzma_stream_encoder 10 | obj:/usr/lib/x86_64-linux-gnu/libarchive.so.13.2.2 11 | fun:archive_write_open 12 | fun:tar_create 13 | fun:main 14 | } 15 | -------------------------------------------------------------------------------- /test/valgrind/musl.supp: -------------------------------------------------------------------------------- 1 | # Suppressions for musl libc 2 | # See: https://www.openwall.com/lists/musl/2017/06/15/4 3 | 4 | { 5 | musl-dynlink-false-positive1 6 | Memcheck:Leak 7 | match-leak-kinds: reachable 8 | fun:calloc 9 | fun:load_direct_deps 10 | fun:load_deps.part.0 11 | fun:load_deps 12 | fun:__dls3 13 | fun:__dls2 14 | } 15 | 16 | { 17 | musl-dynlink-false-positive2 18 | Memcheck:Leak 19 | match-leak-kinds: reachable 20 | fun:calloc 21 | fun:load_library 22 | fun:load_direct_deps 23 | fun:load_deps.part.0 24 | fun:load_deps 25 | fun:__dls3 26 | fun:__dls2 27 | } 28 | 29 | { 30 | musl-dynlink-false-positive3 31 | Memcheck:Leak 32 | match-leak-kinds: reachable 33 | fun:calloc 34 | fun:load_library 35 | fun:load_preload 36 | fun:__dls3 37 | fun:__dls2 38 | } 39 | --------------------------------------------------------------------------------