This commit is contained in:
Night Kaly 2024-05-21 21:24:01 +01:00
parent 16b79c8eaa
commit 83e3e3ca78
Signed by: night0721
GPG key ID: 957D67B8DB7A119B
9 changed files with 450 additions and 40 deletions

2
.gitignore vendored
View file

@ -1,3 +1,3 @@
sup
bob
*.o
*.tar.gz

View file

@ -3,19 +3,20 @@
CC = cc
VERSION = 1.0
TARGET = sup
TARGET = bob
MANPAGE = $(TARGET).1
PREFIX ?= /usr/local
BINDIR = $(PREFIX)/bin
MANDIR = $(PREFIX)/share/man/man1
# Flags
CFLAGS = -O3 -march=native -mtune=native -pipe -s -std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600
LDFLAGS = $(shell pkg-config --libs libcurl)
CFLAGS = -O3 -march=native -mtune=native -pipe -g -std=c99 -pedantic -Wall -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600
SRC = sup.c
SRC = bob.c
$(TARGET): $(SRC)
$(CC) $(SRC) -o $@ $(CFLAGS)
$(CC) $(SRC) -o $@ $(CFLAGS) $(LDFLAGS)
dist:
mkdir -p $(TARGET)-$(VERSION)

View file

@ -1,6 +1,24 @@
# sup
# bob
sup(System Utilities Packager) is a package manager that is for system utilities software.
bob is a **B**inary **O**nly package manager that is for managing my publicly published softwares. Sample repository can be found on [here](https://github.com/night0721/bob-packages), only works on musl-libc system.
It is recommended to have `XDG_DATA_HOME` defined to store the database for bob, otherwise database file would be created at `~/.cache`
# Customizing
You may use your own Github repository for supplying the binaries, or other platforms but requiring you to modify the source to use other URL to download.
All these customizations can be done in `bob.h`
# Usage
```sh
Usage:
(install|i|add) <package> Install a package
(uninstall|d|del) <package> Uninstall a package
(update|u) <package> Update a package
(search|s) <package> Search for a package
(list|l) [all] List all packages installed/available
```
# Dependencies
@ -20,4 +38,4 @@ $ make
Contributions are welcomed, feel free to open a pull request.
# License
This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/sup/blob/master/LICENSE) for more information.
This project is licensed under the GNU Public License v3.0. See [LICENSE](https://github.com/night0721/bob/blob/master/LICENSE) for more information.

26
bob.1 Normal file
View file

@ -0,0 +1,26 @@
.TH bob 1 bob\-1.0.0
.SH NAME
bob \- Binary Only Packager Manager
.SH SYNOPSIS
.B bob
.RB [ install|i|add ]
.RB [ uninstall|d|del ]
.RB [ update|u ]
.RB [ search|s ]
.RB [ list|s ]
.SH DESCRIPTION
bob is a Binary Only package manager that is for managing my publicly published softwares.
.PP
.SH USAGE
.
.nf
(install|i|add) <package> Install a package
(uninstall|d|del) <package> Uninstall a package
(update|u) <package> Update a package
(search|s) <package> Search for a package
(list|l) [all] List all packages installed/available
.SH AUTHOR
Made by Night Kaly
.B <night@night0721.xyz>

377
bob.c Normal file
View file

@ -0,0 +1,377 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <curl/curl.h>
#include "bob.h"
void debug(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, GRN "[bob] " CRESET);
vfprintf(stderr, fmt, args);
va_end(args);
}
void error(const char *msg, ...)
{
va_list args;
va_start(args, msg);
fprintf(stderr, RED "[bob] " CRESET);
vfprintf(stderr, msg, args);
va_end(args);
}
/*
* For curl to write data
*/
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
char *get_db_path()
{
char *db_dir = getenv("XDG_DATA_HOME");
char *db_path = malloc(PATH_MAX * sizeof(char));
if (db_path == NULL) {
error("Error allocating memory\n");
exit(EXIT_FAILURE);
}
/* make db idr at .cache/bob if XDG_DATA_HOME isn't defined */
if (db_dir) {
snprintf(db_path, PATH_MAX, "%s/bob", db_dir);
} else {
snprintf(db_path, PATH_MAX, "%s/.cache/bob", getenv("HOME"));
}
/* create the directory if it doesn't exist */
mkdir(db_path, 0755);
strcat(db_path, "/bob.db");
return db_path;
}
/*
* act = 0 == delete
* act = 1 == act
*/
int update_package_list(char *pkg, int act)
{
char *db_path = get_db_path();
FILE *db_file = fopen(db_path, "a+");
if (db_file == NULL) {
error("fopen: %s\n", strerror(errno));
free(db_path);
return 1;
}
char line[MAX_PKG_NAME_LENGTH];
char **entries = malloc(MAX_PKGS * sizeof(char *));
int entries_count = 0;
if (entries == NULL) {
error("Error allocating memory\n");
free(db_path);
return 1;
}
int found = 0;
while (fgets(line, sizeof(line), db_file)) {
line[strcspn(line, "\n")] = '\0';
char *name = strdup(line);
if (name == NULL) {
error("Error allocating memory\n");
free(db_path);
free(entries);
return 1;
}
if (strcmp(line, pkg) == 0) {
found = 1;
/* don't add to entries if we are looking for it */
if (!act) continue;
}
entries[entries_count] = name;
entries_count++;
}
if (act) {
if (!found) {
/* not duplicate and adding */
fprintf(db_file, "%s\n", pkg);
}
goto done;
} else {
if (!found) {
/* deleting but not here */
error("Package %s is not installed\n", pkg);
fclose(db_file);
free(db_path);
free(entries);
return 1;
} else {
freopen(db_path, "w", db_file);
for (int i = 0; i < entries_count; i++) {
fprintf(db_file, "%s\n", entries[i]);
free(entries[i]);
}
}
}
done:
fclose(db_file);
free(db_path);
free(entries);
return 0;
}
int download_file(char *pkg)
{
long response_code;
char url[256], dest[PATH_MAX];
snprintf(dest, sizeof(dest), "%s%s", DEST_DIR, pkg);
CURL *curl = curl_easy_init();
if (curl) {
FILE *dest_file = fopen(dest, "wb");
if (dest_file == NULL) {
error("fopen: %s\n", strerror(errno));
return 1;
}
snprintf(url, sizeof(url), "https://raw.githubusercontent.com/%s/%s/%s", REPO, BRANCH, pkg);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, dest_file);
CURLcode res = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
int success = 0;
if (res != CURLE_OK || response_code != 200) {
error("Failed to download %s: %s (Error code %ld)\n", url, curl_easy_strerror(res), response_code);
fclose(dest_file);
remove(dest);
} else {
fclose(dest_file);
debug("Downloaded %s to %s\n", url, dest);
/* make binary exectuable */
if (chmod(dest, 0755) != 0) {
error("chmod: %s\n", strerror(errno));
} else {
debug("Made %s executable\n", pkg);
/* add to package list */
if (update_package_list(pkg, 1) == 0) {
debug("Added %s to package list\n", pkg);
success = 1;
} else {
error("Failed to add %s to package list\n", pkg);
}
}
}
curl_easy_cleanup(curl);
if (!success) {
return 1;
} else {
return 0;
}
}
return 1;
}
int install_package(char *pkg)
{
if (strcmp(pkg, "index") == 0) {
error("'index' is reserved and cannot be installed\n");
}
debug("Installing %s\n", pkg);
if (download_file(pkg) == 0) {
debug("Installation completed\n");
return 0;
} else {
error("Fail to install %s\n", pkg);
return 1;
}
}
int uninstall_package(char *pkg)
{
char dest[PATH_MAX];
snprintf(dest, sizeof(dest), "%s%s", DEST_DIR, pkg);
/* delete from package list */
if (update_package_list(pkg, 0) == 0) {
debug("Removed %s from package list\n", pkg);
} else {
error("Failed to remoe %s from package list\n", pkg);
return 1;
}
debug("Uninstalling %s\n", pkg);
if (remove(dest) == 0) {
debug("Uninstalled %s\n", pkg);
} else {
error("remove: %s\n", strerror(errno));
return 1;
}
return 0;
}
void update_package(char *pkg)
{
if (uninstall_package(pkg) == 0) {
install_package(pkg);
} else {
error("Cannot continue, please retry\n");
}
}
char **search_index()
{
long response_code;
char temp_path[] = "/tmp/bob_index";
char url[256];
snprintf(url, sizeof(url), "https://raw.githubusercontent.com/%s/%s/index", REPO, BRANCH);
CURL *curl = curl_easy_init();
if (curl) {
FILE *temp_f = fopen(temp_path, "wb");
if (temp_f == NULL) {
error("fopen: %s\n", strerror(errno));
curl_easy_cleanup(curl);
remove(temp_path);
return NULL;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, temp_f);
CURLcode res = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
fclose(temp_f);
if (res != CURLE_OK || response_code != 200) {
fprintf(stderr, "Failed to download repository index: %s (Error code %ld)\n", curl_easy_strerror(res), response_code);
curl_easy_cleanup(curl);
remove(temp_path);
return NULL;
}
curl_easy_cleanup(curl);
}
FILE *index_file = fopen(temp_path, "r");
if (index_file == NULL) {
error("fopen: %s\n", strerror(errno));
remove(temp_path);
return NULL;
}
char **entries = malloc(MAX_PKGS * sizeof(char *));
int entries_count = 0;
if (entries == NULL) {
error("Error allocating memory\n");
return NULL;
}
char line[MAX_PKG_NAME_LENGTH];
while (fgets(line, sizeof(line), index_file)) {
line[strcspn(line, "\n")] = '\0';
char *name = strdup(line);
if (name == NULL) {
error("Error allocating memory\n");
return NULL;
}
entries[entries_count] = name;
entries_count++;
}
entries[entries_count] = NULL;
fclose(index_file);
remove(temp_path);
return entries;
}
void search_package(char *pkg)
{
debug("Searching for package: %s\n", pkg);
char **index = search_index();
for (int i = 0; index[i] != NULL; i++) {
if (strcmp(pkg, index[i]) == 0) {
debug("Package \"%s\" found in repository index\n", pkg);
return;
}
}
error("Package \"%s\" not found in repository index\n", pkg);
}
void list_package(char *pkg)
{
if (pkg == NULL) {
/* list installed packages */
char *db_path = get_db_path();
FILE *db_file = fopen(db_path, "r");
if (db_file == NULL) {
error("fopen: %s\n", strerror(errno));
free(db_path);
return;
}
char line[MAX_PKG_NAME_LENGTH];
while (fgets(line, sizeof(line), db_file)) {
line[strcspn(line, "\n")] = '\0';
printf("%s\n", line);
}
} else if (strcmp(pkg, "all") == 0) {
/* list all available packages */
char **index = search_index();
for (int i = 0; index[i] != NULL; i++) {
printf("%s\n", index[i]);
}
} else {
error("Unknown option\n");
}
}
int main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "bob %s\n\nUsage:\n", VERSION);
fprintf(stderr, " (install|i|add) <package>\tInstall a package\n");
fprintf(stderr, " (uninstall|d|del) <package>\tUninstall a package\n");
fprintf(stderr, " (update|u) <package>\t\tUpdate a package\n");
fprintf(stderr, " (search|s) <package>\t\tSearch for a package\n");
fprintf(stderr, " (list|l) [all] \t\tList all packages installed/available\n");
return EXIT_FAILURE;
}
char *command = argv[1];
char *pkg = (argc > 2) ? argv[2] : NULL;
if ((strcmp(command, "install") == 0 || strcmp(command, "add") == 0 || strcmp(command, "i") == 0) && pkg) {
install_package(pkg);
} else if ((strcmp(command, "uninstall") == 0 || strcmp(command, "del") == 0 || strcmp(command, "d") == 0) && pkg) {
uninstall_package(pkg);
} else if ((strcmp(command, "update") == 0 || strcmp(command, "u") == 0) && pkg) {
update_package(pkg);
} else if ((strcmp(command, "search") == 0 || strcmp(command, "s") == 0) && pkg) {
search_package(pkg);
} else if ((strcmp(command, "list") == 0 || strcmp(command, "l") == 0)) {
list_package(pkg);
} else {
error("Unknown command or package name missing\n");
}
return EXIT_SUCCESS;
}

20
bob.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef BOB_H_H
#define BOB_H_H
#define VERSION "1.0.0"
/* Not recommend modifiying this unless you know what you are doing */
#define MAX_PKG_NAME_LENGTH 128
#define MAX_PKGS 128
/* Must be github repo */
#define REPO "night0721/bob-packages"
#define BRANCH "main"
#define DEST_DIR "/usr/local/bin/"
#define RED "\033[0;31m"
#define GRN "\033[0;32m"
#define CRESET "\033[0m"
#endif

14
sup.1
View file

@ -1,14 +0,0 @@
.TH sup 1 sup\-1.0.0
.SH NAME
sup \- System Utilities Packager
.SH SYNOPSIS
.B sup
.SH DESCRIPTION
sup is a package manager that is for system utilities software.
.SH OPTIONS
.SH AUTHOR
Made by Night Kaly
.B <night@night0721.xyz>

14
sup.c
View file

@ -1,14 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
int main(int argc, char **argv)
{
printf("sup!\n");
return 0;
}

4
sup.h
View file

@ -1,4 +0,0 @@
#ifndef SUP_H_H
#define SUP_H_H
#endif