370 lines
9.1 KiB
C
370 lines
9.1 KiB
C
#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, "\033[0;32m[bob]\033[0m ");
|
|
vfprintf(stderr, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void error(const char *msg, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, msg);
|
|
fprintf(stderr, "\033[0;31m[bob]\033[0m ");
|
|
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_path = malloc(12);
|
|
if (db_path == NULL) {
|
|
error("Error allocating memory\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
snprintf(db_path, 9, "/lib/bob");
|
|
|
|
/* create the directory if it doesn't exist */
|
|
mkdir(db_path, 0755);
|
|
strcat(db_path, "/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), "%s/%s", URL, 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), "%s/index", URL);
|
|
|
|
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;
|
|
}
|