aboutsummaryrefslogtreecommitdiff
path: root/spm.c
diff options
context:
space:
mode:
Diffstat (limited to 'spm.c')
-rw-r--r--spm.c343
1 files changed, 264 insertions, 79 deletions
diff --git a/spm.c b/spm.c
index 152f3f1..478dbb5 100644
--- a/spm.c
+++ b/spm.c
@@ -5,26 +5,18 @@
#include <ctype.h>
#include <errno.h>
+#include <glob.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
-#include <glob.h>
+#include <time.h>
+#include "config.h"
+#include "spm.h"
-#define SYSERROR stderr, "%s:%s:%d: %s\n", __FILE__, __FUNCTION__, __LINE__, strerror(errno)
-#define DIRSEP_WIN32 '\\'
-#define DIRSEP_UNIX '/'
-#if defined(_WIN32)
-#define DIRSEP DIRSEP_WIN32
-#define NOT_DIRSEP DIRSEP_UNIX
-#else
-#define DIRSEP DIRSEP_UNIX
-#define NOT_DIRSEP DIRSEP_WIN32
-#endif
-#define PKG_DIR "../pkgs"
/**
* A wrapper for `popen()` that executes non-interactive programs and reports their exit value.
@@ -33,33 +25,41 @@
* ~~~{.c}
* int fd = 1; // stdout
* const char *log_file = "log.txt";
- * char *buf;
+ * Process *proc_info;
* int status;
*
* // Send stderr to stdout
- * status = shell(buf, "foo 2>&1");
+ * shell(&proc_info, SHELL_OUTPUT, "foo 2>&1");
* // Send stdout and stderr to /dev/null
- * status = shell(buf, "bar &>/dev/null");
+ * shell(&proc_info, SHELL_OUTPUT,"bar &>/dev/null");
* // Send stdout from baz to log.txt
- * status = shell(buf, "baz %d>%s", fd, log_file);
+ * shell(&proc_info, SHELL_OUTPUT, "baz %d>%s", fd, log_file);
* // Do not record or redirect output from any streams
- * status = shell(NULL, "biff");
+ * shell(&proc_info, SHELL_DEFAULT, "biff");
* ~~~
*
- * @param buf buffer to hold program output (to ignore output, set to NULL)
+ * @param Process uninitialized `Process` struct will be populated with process data
* @param options change behavior of the function
* @param fmt shell command to execute (accepts `printf` style formatters)
* @param ... variadic arguments (used by `fmt`)
- * @return shell exit code
*/
-#define SHELL_DEFAULT 1 << 0
-#define SHELL_OUTPUT 1 << 1
-int shell(char **buf, u_int8_t option, const char *fmt, ...) {
+void shell(Process **proc_info, u_int64_t option, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
- FILE *proc;
- int status = -1;
+ size_t bytes_read = 0;
+ size_t i = 0;
+ size_t new_buf_size = 0;
+ clockid_t clkid = CLOCK_REALTIME;
+ FILE *proc = NULL;
+
+ (*proc_info) = (Process *)calloc(1, sizeof(Process));
+ if (!(*proc_info)) {
+ fprintf(SYSERROR);
+ exit(errno);
+ }
+ (*proc_info)->returncode = -1;
+
char *outbuf = (char *)calloc(1, sizeof(char));
if (!outbuf) {
fprintf(SYSERROR);
@@ -73,57 +73,86 @@ int shell(char **buf, u_int8_t option, const char *fmt, ...) {
vsnprintf(cmd, PATH_MAX, fmt, args);
+ if (option & SHELL_BENCHMARK) {
+ if (clock_gettime(clkid, &(*proc_info)->start_time) == -1) {
+ perror("clock_gettime");
+ exit(errno);
+ }
+ }
+
proc = popen(cmd, "r");
if (!proc) {
- return status;
+ return;
+ }
+
+ if (option & SHELL_BENCHMARK) {
+ if (clock_gettime(clkid, &(*proc_info)->stop_time) == -1) {
+ perror("clock_gettime");
+ exit(errno);
+ }
+ (*proc_info)->time_elapsed = ((*proc_info)->stop_time.tv_sec - (*proc_info)->start_time.tv_sec)
+ + ((*proc_info)->stop_time.tv_nsec - (*proc_info)->start_time.tv_nsec) / 1E9;
}
- size_t bytes_read = 0;
- size_t i = 0;
- size_t new_buf_size = 0;
if (option & SHELL_OUTPUT) {
- *buf = (char *)calloc(BUFSIZ, sizeof(char));
+ (*proc_info)->output = (char *)calloc(BUFSIZ, sizeof(char));
- while (!feof(proc)) {
- *outbuf = fgetc(proc);
+ while ((*outbuf = fgetc(proc)) != EOF) {
if (i >= BUFSIZ) {
new_buf_size = BUFSIZ + (i + bytes_read);
- (*buf) = (char *)realloc((*buf), new_buf_size);
+ (*proc_info)->output = (char *)realloc((*proc_info)->output, new_buf_size);
i = 0;
}
- if (isascii(*outbuf)) {
- (*buf)[bytes_read] = *outbuf;
+ if (*outbuf) {
+ (*proc_info)->output[bytes_read] = *outbuf;
}
bytes_read++;
i++;
}
}
- status = pclose(proc);
+ (*proc_info)->returncode = pclose(proc);
va_end(args);
free(outbuf);
free(cmd);
- return status;
-};
+}
-/*
+/**
+ * Free process resources allocated by `shell()`
+ * @param proc_info `Process` struct
+ */
+void shell_free(Process *proc_info) {
+ free(proc_info->output);
+ free(proc_info);
+}
+
+/**
+ * Extract a single file from a tar archive into a directory
+ *
+ * @param archive path to tar archive
+ * @param filename known path inside the archive to extract
+ * @param destination where to extract file to (must exist)
+ * @return
+ */
int tar_extract_file(const char *archive, const char* filename, const char *destination) {
+ Process *proc = NULL;
int status = -1;
char cmd[PATH_MAX];
char output[3];
+
sprintf(cmd, "tar xf %s %s -C %s 2>&1", archive, filename, destination);
- FILE *proc = popen(cmd, "r");
+
+ shell(&proc, SHELL_OUTPUT, cmd);
if (!proc) {
- return status;
- }
- while (!feof(proc)) {
- fgets(output, 2, proc);
- printf("%s", output);
+ printf("%s\n", proc->output);
+ fprintf(SYSERROR);
+ return -1;
}
- putchar('\n');
- return pclose(proc);
+ status = proc->returncode;
+ shell_free(proc);
+
+ return status;
};
- */
/**
* glob callback function
@@ -160,7 +189,7 @@ int num_chars(const char *sptr, int ch) {
* @return 0 = success, -1 = failure
*/
int startswith(const char *sptr, const char *pattern) {
- for (int i = 0; i < strlen(pattern); i++) {
+ for (size_t i = 0; i < strlen(pattern); i++) {
if (sptr[i] != pattern[i]) {
return -1;
}
@@ -212,13 +241,40 @@ char *normpath(const char *path) {
}
/**
+ * Deletes any characters matching `chars` from `sptr` string
+ *
+ * @param sptr string to be modified in-place
+ * @param chars a string containing characters (e.g. " \n" would delete whitespace and line feeds)
+ */
+void strchrdel(char *sptr, const char *chars) {
+ while (*sptr != '\0') {
+ for (int i = 0; chars[i] != '\0'; i++) {
+ if (*sptr == chars[i]) {
+ memmove(sptr, sptr + 1, strlen(sptr));
+ }
+ }
+ sptr++;
+ }
+}
+
+int64_t strchroff(char *sptr, int ch) {
+ char *tmp = sptr;
+ while (*tmp != '\0') {
+ if (*tmp == ch) {
+ return tmp - sptr;
+ }
+ tmp++;
+ }
+ return -1;
+}
+/**
* This function scans `sptr` from right to left removing any matches to `suffix`
* from the string.
*
* @param sptr string to be modified
* @param suffix string to be removed from `sptr`
*/
-void strip(char *sptr, const char *suffix) {
+void substrdel(char *sptr, const char *suffix) {
if (!sptr || !suffix) {
return;
}
@@ -237,24 +293,7 @@ void strip(char *sptr, const char *suffix) {
// Purge the suffix
memset(target, '\0', suffix_len);
// Recursive call continues removing suffix until it is gone
- strip(sptr, suffix);
- }
-}
-
-/**
- * Deletes any characters matching `chars` from `sptr` string
- *
- * @param sptr string to be modified in-place
- * @param chars a string containing characters (e.g. " \n" would delete whitespace and line feeds)
- */
-void strchrdel(char *sptr, const char *chars) {
- while (*sptr != '\0') {
- for (int i = 0; chars[i] != '\0'; i++) {
- if (*sptr == chars[i]) {
- memmove(sptr, sptr + 1, strlen(sptr));
- }
- }
- sptr++;
+ strip(sptr);
}
}
@@ -320,33 +359,179 @@ char *find_package(const char *filename) {
return find_file(PKG_DIR, filename);
}
-int main() {
+char** split(char *sptr, const char* delim)
+{
+ int split_alloc = 0;
+ for (int i = 0; i < strlen(delim); i++) {
+ split_alloc += num_chars(sptr, delim[i]);
+ }
+ char **result = (char **)calloc(split_alloc + 2, sizeof(char *));
+
+ int i = 0;
+ //char *token = strsep(&sptr, delim);
+ char *token = NULL;
+ while((token = strsep(&sptr, delim)) != NULL) {
+ result[i] = (char *)calloc(1, sizeof(char) * strlen(token) + 1);
+ strcpy(result[i], token);
+ i++;
+ }
+ return result;
+}
+
+void split_free(char **ptr) {
+ for (int i = 0; ptr[i] != NULL; i++) {
+ free(ptr[i]);
+ }
+ free(ptr);
+}
+
+char *substring_between(char *sptr, const char *delims) {
+ int delim_count = strlen(delims);
+ if (delim_count != 2) {
+ return NULL;
+ }
+
+ char *start = strpbrk(sptr, &delims[0]);
+ char *end = strpbrk(sptr, &delims[1]);
+ if (!start || !end) {
+ return NULL;
+ }
+ start++; // ignore leading delimiter
+
+ long int length = end - start;
+
+ char *result = (char *)calloc(1, sizeof(char) * length);
+ if (!result) {
+ return NULL;
+ }
+
+ char *tmp = result;
+ while (start != end) {
+ *tmp = *start;
+ tmp++;
+ start++;
+ }
+
+ return result;
+}
+
+/**
+ * Uses `readelf` to determine whether a RPATH or RUNPATH is present
+ *
+ * TODO: Replace with OS-native solution(s)
+ *
+ * @param filename path to executable or library
+ * @return -1=OS error, 0=has rpath, 1=not found
+ */
+int has_rpath(const char *filename) {
+ int result = 1;
+ Process *proc_info = NULL;
+ char *path = strdup(filename);
+ const char *preamble = "readelf -d";
+
+ // sanitize input path
+ strchrdel(path, "&;|");
+
+ char sh_cmd[strlen(preamble) + 1 + strlen(path) + 1];
+ memset(sh_cmd, '\0', sizeof(sh_cmd));
+
+ sprintf(sh_cmd, "%s %s", preamble, path);
+ free(path);
+
+ shell(&proc_info, SHELL_OUTPUT, sh_cmd);
+ if (!proc_info) {
+ fprintf(SYSERROR);
+ return -1;
+ }
+
+ strip(proc_info->output);
+ char **lines = split(proc_info->output, "\n");
+ for (int i = 0; lines[i] != NULL; i++) {
+ if (strstr(lines[i], "RPATH") || strstr(lines[i], "RUNPATH")) {
+ result = 0;
+ }
+ }
+ shell_free(proc_info);
+ split_free(lines);
+ return result;
+}
+
+/**
+ * Parses `readelf` output and returns an RPATH or RUNPATH if one is defined
+ *
+ * TODO: Replace with OS-native solution(s)
+ *
+ * @param filename path to executable or library
+ * @return RPATH string, NULL=error (caller is responsible for freeing memory)
+ */
+char *get_rpath(const char *filename) {
+ if ((has_rpath(filename)) != 0) {
+ return NULL;
+ }
+
+ Process *proc_info = NULL;
+ char *path = strdup(filename);
+ const char *preamble = "readelf -d";
+ char *rpath = NULL;
+
+ // sanitize input path
+ strchrdel(path, "&;|");
+
+ char sh_cmd[strlen(preamble) + 1 + strlen(path) + 1];
+ memset(sh_cmd, '\0', sizeof(sh_cmd));
+
+ sprintf(sh_cmd, "%s %s", preamble, path);
+ free(path);
+
+ shell(&proc_info, SHELL_OUTPUT, sh_cmd);
+ if (!proc_info) {
+ fprintf(SYSERROR);
+ return NULL;
+ }
+
+ strip(proc_info->output);
+ char **lines = split(proc_info->output, "\n");
+ for (int i = 0; lines[i] != NULL; i++) {
+ if (strstr(lines[i], "RPATH") || strstr(lines[i], "RUNPATH")) {
+ rpath = substring_between(lines[i], "[]");
+ break;
+ }
+ }
+ shell_free(proc_info);
+ split_free(lines);
+ return rpath;
+}
+
+int main(int argc, char *argv[]) {
+ // not much to see here yet
+ // at the moment this will all be random tests, for better or worse
+ // everything here is subject to change without notice
+
const char *pkg_name = "python";
char *package = NULL;
package = find_package(pkg_name);
if (package != NULL) {
printf("Package found: %s\n", package);
free(package);
- }
- else {
- fprintf(stderr,"Package does not exist: %s\n", pkg_name);
+ } else {
+ fprintf(stderr, "Package does not exist: %s\n", pkg_name);
}
const char *testpath = "x:\\a\\b\\c";
const char *teststring = "this is test!";
+ const char *testprog = "/tmp/a.out";
char *normalized = normpath(testpath);
- char *buf = NULL;
printf("%s becomes %s\n", testpath, normalized);
printf("%d\n", startswith(testpath, "x:\\"));
printf("%d\n", endswith(teststring, "test!"));
- //tar_extract_file("one", "two", testpath);
-
- int retval = -1;
- retval = shell(&buf, SHELL_OUTPUT, "env; env; env; env; env; env; env; env");
- strip(buf, "\n");
- printf("%s\n", buf);
-
free(normalized);
+
+ if ((has_rpath(testprog)) == 0) {
+ printf("RPATH found\n");
+ char *rpath = get_rpath(testprog);
+ printf("RPATH is: %s\n", rpath);
+ free(rpath);
+ }
return 0;
} \ No newline at end of file