From 10af9da21bc004449f4148ccf63fb9aa06e44bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Tue, 12 Sep 2023 22:05:38 +0200 Subject: Work on c replacement for wiki script. --- Makefile | 13 ++ wiki.c | 451 +++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 397 insertions(+), 67 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..aa84950 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CFLAGS = -Wall -pedantic -std=c2x -ggdb \ + -D_POSIX_C_SOURCE=200809L \ + -D_XOPEN_SOURCE=700 \ + -D_DEFAULT_SOURCE \ + -D_BSD_SORUCE \ + -pthread \ + -I/usr/local/include +#### -fsanitize=thread \ +#### -fsanitize=undefined +LDFLAGS = -pthread -L/usr/local/lib +LDLIBS = -lpthread + +wiki: wiki.c diff --git a/wiki.c b/wiki.c index b4e6b65..e1805e0 100644 --- a/wiki.c +++ b/wiki.c @@ -1,20 +1,34 @@ + + #include #include #include +#include +#include +#include +#include #include #include #include #include +#include #include +#include +#include #include #include -#include +#define __BSD_VISIBLE 1 +#include +#undef __BSD_VISIBLE + +#include +#include +#include // #define WIKIROOT "~/wiki/" #define DATE_FORMAT "%a %d %b. %Y %H:%M:%S %Z" -#if __STDC_VERSION__ >= 202311L -#else +#if __STDC_VERSION__ < 202311L #define typeof __typeof__ #endif @@ -26,6 +40,8 @@ typedef char **strlist; +static pthread_mutex_t *stdout_lock = NULL, *stderr_lock = NULL; + /** * Collect all wikis under WIKIROOT. * @param buf @@ -78,7 +94,7 @@ void print_available_wikis(); /** Print help string for the program to stdout */ void print_help(); -/** +/** * Find the directory to search for wikis in. * Returns a string which must be freed. * */ @@ -98,26 +114,111 @@ char *strconcat(int count, const char *arr[]); * subcommand, or one of our special commands. * Remaining member will be used as arguments to that subcommand. */ -void wiki_do(const char *wiki_root, - const char *wiki, +int wiki_do(const char *wiki_root, + const char *wiki, + int argc, const char *command[]); -int main (int argc, char *argv[]) { +void write_stderr(const char *wiki, const char *fmt, ...); +void write_stdout(const char *wiki, const char *fmt, ...); + +struct thread_data { + int stdout; + int stderr; + int peer_pid; + char *wiki; + pthread_cond_t *cond; +}; + +static void *start_thread(void *d) { + + struct thread_data *data = d; + + fprintf(stderr, "%i, %i\n", data->stdout, data->stderr); + + FILE *out = fdopen(data->stdout, "w"); + FILE *err = fdopen(data->stderr, "w"); + + char *line = NULL; + size_t linecapp; + + fd_set fds; + FD_SET(data->stdout, &fds); + FD_SET(data->stderr, &fds); + + ssize_t len; + + // kill(data->peer_pid, SIGUSR1); + pthread_cond_signal(data->cond); + munmap(data->cond, sizeof data->cond); + + struct timeval timeout = { + .tv_sec = 1, + }; + + int count; + + for (;;) { + printf("Running select:\n"); + if ((count = select(2, &fds, NULL, NULL, NULL)) == -1) { + fprintf(stderr, "Error in select: %s\n", strerror(errno)); + write_stderr(data->wiki, "Error in select: %s\n", strerror(errno)); + continue; + } + fprintf(stderr, "Got %i!\n", count); + +#if 1 + if (FD_ISSET(data->stdout, &fds)) { + fprintf(stderr, "on stdout\n"); + errno = 0; + if ((len = getline(&line, &linecapp, out)) == -1) { + if (errno) { + write_stderr(data->wiki, "Failed reading stdout: %s", strerror(errno)); + } + /* descriptor was closed */ + write_stderr(data->wiki, "stderr was closed"); + } else { + write_stdout(data->wiki, "%s", line); + } + } + + if (FD_ISSET(data->stderr, &fds)) { + fprintf(stderr, "on stderr\n"); + errno = 0; + if ((len = getline(&line, &linecapp, err)) == -1) { + if (errno) { + write_stderr(data->wiki, "Failed reading stderr: %s", strerror(errno)); + } + /* descriptor was closed */ + write_stderr(data->wiki, "stdout was closed"); + } else { + write_stderr(data->wiki, "%s", line); + } + } +#endif + } + + return NULL; +} + +int main (int argc, const char *argv[]) { + truncate("/tmp/hugo/log", 0); + printf("pid = %i\n", getpid()); /* At most every other argument can be a requested wiki */ strlist wiki_list = malloc(sizeof(*wiki_list) * argc / 2); + /** index into wiki_list, pointing at the next empty slot */ int wiki_list_ptr = 0; + /** Number of wikis to work on */ + int wiki_count = 0; + + char *wiki_root = get_wikiroot(); int arg = 1; for (; arg < argc; arg++) { if (cmp("--wiki", argv[arg]) || cmp("-w", argv[arg])) { - /* realpath */ - char *buf = realpath(argv[++arg], NULL); - if (buf == NULL) { - printf("Failed opening %s: %s\n", argv[arg - 1], strerror(errno)); - } else { - wiki_list[wiki_list_ptr++] = buf; - } + wiki_list[wiki_list_ptr++] = (char *) argv[++arg]; + ++wiki_count; continue; } else if (cmp("--list", argv[arg]) || cmp("-l", argv[arg])) { @@ -132,62 +233,185 @@ int main (int argc, char *argv[]) { } } - char *wiki_root; + wiki_count = wiki_list_ptr; if (wiki_list_ptr == 0) { free(wiki_list); - wiki_root = get_wikiroot(); - gather_wikis(wiki_root, &wiki_list); - } - free(wiki_root); + wiki_count = gather_wikis(wiki_root, &wiki_list); + } /* If no further arguments are given, edit the given wiki */ -#if 0 if (arg == argc) { char *wiki; - strlist wikis = NULL; - if (wiki_list_ptr != 0) { - wiki = wiki_list[0]; - } else { - int wiki_count = gather_wikis(&wikis); - if (wiki_count < 0) { - exit(1); - } - if (wiki_count == 0) { - fprintf(stderr, "No wikis found\n"); - } - wiki = wikis[0]; + if (wiki_count == 0) { + fprintf(stderr, "No wikis found\n"); + return 1; } - // TODO - // glob("${WIKIROOT}/${wiki}/index.*") - // execp("vim", "vim", - if (wikis) free(wikis); + + wiki = wiki_list[0]; + + char *path = malloc(1024); + sprintf(path, "%s/%s/index.wiki", + wiki_root, wiki); + execlp("vim", "vim", path, NULL); } else { - /* If further arguments are given */ - const char *cmd = argv[arg]; - strlist final_list; - int wiki_count; - if (wiki_list_ptr != 0) { - final_list = wiki_list; - wiki_count = wiki_list_ptr; - } else { - free(wiki_list); - wiki_count = gather_wikis(&final_list); - if (wiki_count < 0) { - /* TODO handle error */ + /* If further arguments are given, run these on each wiki */ + + int ret; + + /* locks heap allocated to allow them to default to NULL */ + stdout_lock = malloc(sizeof(*stdout_lock)); + stderr_lock = malloc(sizeof(*stderr_lock)); + + if ((ret = pthread_mutex_init(stdout_lock, NULL))) { + fprintf(stderr, "Failed creating stdout mutex: %s\n", strerror(ret)); + stdout_lock = NULL; + } + + if ((ret = pthread_mutex_init(stderr_lock, NULL))) { + fprintf(stderr, "Failed creating stderr mutex: %s\n", strerror(ret)); + stderr_lock = NULL; + } + + int *pids = malloc(sizeof(*pids) * wiki_count); + + for (int i = 0; i < wiki_count; i++) { + int stdout_pipes[2], stderr_pipes[2]; + + if (pipe(stdout_pipes) == -1) { + fprintf(stderr, "Failed opening stdout pipe for '%s': %s\n", + wiki_list[i], strerror(errno)); + continue; + }; + + if (pipe(stderr_pipes) == -1) { + fprintf(stderr, "Failed opening stderr pipe for '%s': %s\n", + wiki_list[i], strerror(errno)); + continue; + }; + + pthread_cond_t *cond = mmap(NULL, sizeof (pthread_cond_t), + PROT_READ|PROT_WRITE, + MAP_ANON|MAP_SHARED, + -1, 0); + if (cond == MAP_FAILED) { + fprintf(stderr, "Failed establishing shared memory region: %s\n", + strerror(errno)); + continue; + } + + pthread_cond_init(cond, NULL); + +#define READ_END 0 +#define WRITE_END 1 + + int pid; + switch ((pid = fork())) { + case 0: + ; + FILE *err = fdopen(dup(2), "w"); + + // close(stdout_pipes[READ_END]); + // close(stderr_pipes[READ_END]); + + if (dup2(stdout_pipes[WRITE_END], 1) == -1) { + fprintf(err, "Failed replacing stdout: %s\n", strerror(errno)); + } + + if (dup2(stderr_pipes[WRITE_END], 2) == -1) { + fprintf(err, "Failed replacing stderr: %s\n", strerror(errno)); + } + + // close(stdout_pipes[WRITE_END]); + // close(stderr_pipes[WRITE_END]); + + + { + pthread_mutex_t cond_mutex; + pthread_mutex_init(&cond_mutex, NULL); + fprintf(err, "Waiting on condition"); + pthread_cond_wait(cond, &cond_mutex); + fprintf(err, "Awoken from condition"); + pthread_mutex_unlock(&cond_mutex); + pthread_mutex_destroy(&cond_mutex); + } + pthread_cond_destroy(cond); + munmap(cond, sizeof cond); + + ret = wiki_do(wiki_root, wiki_list[i], argc - arg, &argv[0] + arg); + exit(ret); + + case -1: + fprintf(stderr, "Failed creating process for '%s': %s\n", + wiki_list[i], strerror(errno)); + continue; + + default: + pids[i] = pid; + + // close(stdout_pipes[WRITE_END]); + // close(stderr_pipes[WRITE_END]); + +#if 0 + + // kill(pid, SIGUSR1); + char buf[10]; + ssize_t res = read(stdout_pipes[0], &buf[0], 2); + printf("'%c' (%i)\n", buf[0], res); + // fcntl(stdout_pipes[0], F_GETFD) != -1 || errno != EBADF; +#endif + +#if 1 + pthread_t thread; + + struct thread_data *data = calloc(sizeof *data, 1); + { + struct thread_data dat = { + .stdout = stdout_pipes[0], + .stderr = stderr_pipes[0], + .wiki = wiki_list[i], + .peer_pid = pid, + .cond = cond, + }; + memcpy(data, &dat, sizeof dat); + } + + if ((ret = pthread_create(&thread, NULL, &start_thread, + (void *) data))) { + fprintf(stderr, "Failed starting thread: %s\n", strerror(ret)); + } +#endif + break; } + } + sleep(3); + for (int i = 0; i < wiki_count; i++) { - /* TODO args */ - wiki_do(wiki_root, final_list[i], args); + fprintf(stderr, "Waiting on %i\n", pids[i]); + int status; + if (waitpid(pids[i], &status, 0) == -1) { + if (errno == ECHILD) { + fprintf(stderr, "No exit status available\n"); + } else { + fprintf(stderr, "Waitpid failed: %s\n", strerror(errno)); + } + } else { + fprintf(stderr, "Child exited %s with status code %i", + WIFEXITED(status) ? "normally" : "abnormaly", + WEXITSTATUS(status)); + if (WIFSIGNALED(status)) { + fprintf(stderr, " by the signal %i", WTERMSIG(status)); + } + fprintf(stderr, "\n"); + } } } -#endif + free(wiki_root); free(wiki_list); - } int gather_wikis(const char *wiki_root, strlist *wiki_list) { @@ -196,7 +420,7 @@ int gather_wikis(const char *wiki_root, strlist *wiki_list) { glob_t pglob = { 0 }; - ret = glob(CALL(strconcat, wiki_root, "/*"), GLOB_TILDE, + ret = glob(CALL(strconcat, wiki_root, "/*"), 0, &errfunc, &pglob); switch (ret) { @@ -220,11 +444,11 @@ int gather_wikis(const char *wiki_root, strlist *wiki_list) { goto gather_wikis_end; } - *wiki_list = malloc(sizeof(*wiki_list) * pglob.gl_matchc); + *wiki_list = malloc(sizeof(*wiki_list) * pglob.gl_pathc); int idx = 0; - for (int i = 0; i < pglob.gl_matchc; i++) { - if (strstr(pglob.gl_pathv[i], "html") != NULL) { + for (int i = 0; i < pglob.gl_pathc; i++) { + if (strstr(pglob.gl_pathv[i], "html") == NULL) { (*wiki_list)[idx++] = basename(pglob.gl_pathv[i]); } } @@ -275,6 +499,7 @@ int commit_ammend (const char* action, const char *msg) { } int git(size_t argc, const char *argv[]) { +#if 0 pid_t pid; int ret; @@ -283,6 +508,7 @@ int git(size_t argc, const char *argv[]) { case -1: break; case 0: /* child */ + ; size_t cmdline_len = ARR_LEN(cmdline_base) + argc; @@ -315,6 +541,8 @@ int git(size_t argc, const char *argv[]) { } return ret; +#endif + return 0; } void print_available_wikis() { @@ -351,11 +579,12 @@ void print_help() { } -void wiki_do(const char *wiki_root, +int wiki_do(const char *wiki_root, const char *wiki, + int argc, const char *command[]) { - if (wiki == NULL) return; + if (wiki == NULL) return 1; char *chdir_dest; if (wiki[0] == '/') { chdir_dest = strdup(wiki); @@ -367,33 +596,67 @@ void wiki_do(const char *wiki_root, printf("failed chdir to %s : %s\n", chdir_dest, strerror(errno)); - exit(1); + return 1; } free(chdir_dest); if (cmp("commit", command[0])) { // (command + 1) - } else + return 1; + } if (cmp("ammend", command[0])) { // (command + 1) - } else + return 0; + } if (cmp("grep", command[0])) { // (command + 1) - } else + return 0; + } if (cmp("go", command[0]) || cmp("g", command[0])) { // (command + 1) - } else + return 0; + } if (cmp("echo", command[0])) { - // (command + 1) - } else { - // (command + 1) + char *buf = malloc(0x100); + // sprintf(buf, "/tmp/hugo/%i", getpid()); + // FILE *log = fopen("/tmp/hugo/log", "a"); + // fprintf(log, "Gonna write!\n"); + + int count = printf("-- %s --\n", wiki); + fflush(stdout); + + // fprintf(log, "Wrote %i bytes\n", count); + // sprintf(buf, "/proc/%i/fd", getpid()); + +#if 0 + DIR *d = opendir(buf); + + struct dirent *ent; + + void *ptr = malloc(sizeof(struct dirent) + 1000 + 1); + while (readdir_r(d, ptr, &ent) == 0) { + if (ent == NULL) break; + memset(buf, 0, 0x100); + readlinkat(dirfd(d), ent->d_name, buf, 0x100); + fprintf(log, "%s -> ", ent->d_name); + fprintf(log, "%s\n", buf); + } + closedir(d); +#endif + // free(buf); + + // :fclose(log); + // fflush()q + return 0; } + + return 0; } char *get_wikiroot() { { - const char *result = getenv("WIKIROOT");; + const char *result = getenv("WIKIROOT"); if (result != NULL) { return strdup(result); } @@ -438,3 +701,57 @@ char *strconcat(int count, const char *arr[]) { return dest; } + + +int locked_write(pthread_mutex_t *lock, FILE *port, const char *fmt, va_list ap) { + int err, ret; + + if (lock && (err = pthread_mutex_lock(lock))) { + fprintf(stderr, "Failed locking mutex: %s\n", strerror(err)); + } + + ret = vfprintf(port, fmt, ap); + + if (lock && (err = pthread_mutex_unlock(lock))) { + fprintf(stderr, "Failed unlocking mutex: %s\n", strerror(err)); + } + + return ret; +} + +char *prepend_wiki(const char *wiki, const char *fmt) { + int wiki_len = strlen(wiki); + int fmt_len = strlen(fmt); + + char *true_fmt = malloc(wiki_len + fmt_len + 1); + + memcpy(true_fmt, wiki, wiki_len); + memcpy(true_fmt + wiki_len, fmt, fmt_len); + true_fmt[wiki_len + fmt_len] = '\0'; + + return true_fmt; +} + +void write_stderr(const char *wiki, const char *fmt, ...) { + char *true_fmt = prepend_wiki(wiki, fmt); + + va_list ap; + + va_start(ap, fmt); + locked_write(stderr_lock, stdout, fmt, ap); + va_end(ap); + + free(true_fmt); +} + +void write_stdout(const char *wiki, const char *fmt, ...) { + char *true_fmt = prepend_wiki(wiki, fmt); + + va_list ap; + + va_start(ap, fmt); + locked_write(stdout_lock, stdout, fmt, ap); + va_end(ap); + + free(true_fmt); +} -- cgit v1.2.3