aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2023-09-12 22:05:38 +0200
committerHugo Hörnquist <hugo@lysator.liu.se>2023-09-12 22:05:38 +0200
commit10af9da21bc004449f4148ccf63fb9aa06e44bc0 (patch)
treef1efe9a503a8640847b706935825adfa44c4281a
parenttis 22 aug 2023 21:28:55 CEST (diff)
downloadwiki-public-10af9da21bc004449f4148ccf63fb9aa06e44bc0.tar.gz
wiki-public-10af9da21bc004449f4148ccf63fb9aa06e44bc0.tar.xz
Work on c replacement for wiki script.
-rw-r--r--Makefile13
-rw-r--r--wiki.c451
2 files changed, 397 insertions, 67 deletions
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 <errno.h>
#include <glob.h>
#include <libgen.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/select.h>
#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
-#include <pwd.h>
+#define __BSD_VISIBLE 1
+#include <sys/mman.h>
+#undef __BSD_VISIBLE
+
+#include <syslog.h>
+#include <dirent.h>
+#include <unistd.h>
// #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);
+}