about summary refs log tree commit diff
path: root/nq.c
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2015-07-31 14:19:32 +0200
committerChristian Neukirchen <chneukirchen@gmail.com>2015-07-31 14:19:32 +0200
commite6b97161209a4a851e63f3cef49e2c156b609d33 (patch)
tree5155a801e969a8f946173e10be63d1915e88e971 /nq.c
downloadnq-e6b97161209a4a851e63f3cef49e2c156b609d33.tar.gz
nq-e6b97161209a4a851e63f3cef49e2c156b609d33.tar.xz
nq-e6b97161209a4a851e63f3cef49e2c156b609d33.zip
Initial import of nq
Diffstat (limited to 'nq.c')
-rw-r--r--nq.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/nq.c b/nq.c
new file mode 100644
index 0000000..ab2e1ee
--- /dev/null
+++ b/nq.c
@@ -0,0 +1,175 @@
+/*
+##% gcc -Wall -g -o $STEM $FILE
+*/
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+static void
+quit(int sig)
+{
+  exit(0);
+}
+
+#include <sys/time.h>
+/*
+char *
+timestamp() {
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  static char buf[10];
+
+  int64_t ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+  buf[9] = 0;
+  int i;
+  for (i = 8; i >= 0; i--) {
+    buf[i] = 'a' + (ms % 26);
+    ms /= 26;
+  }
+  
+  return buf;
+}
+*/
+
+int
+main(int argc, char *argv[])
+{
+  pid_t child;
+  int lockfd;
+  char lockfile[128];
+  int dirfd;
+
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  int64_t ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+  char *path = getenv("NQDIR");
+  if (!path)
+    path = ".";
+
+  dirfd = open(path, O_RDONLY);
+  if (dirfd < 0) {
+    perror("dir open");
+    exit(111);
+  }
+
+  child = fork();
+  if (child == -1) {
+    perror("fork");
+    exit(111);
+  }
+
+  if (child > 0) {
+    // background
+    signal(SIGINT, quit);
+    pause();
+    exit(0);
+  }
+
+  child = fork();
+  if (child == -1) {
+    perror("fork");
+    exit(111);
+  }
+  if (child > 0) {
+    int status;
+    sprintf(lockfile, ",%lx.%d", ms, child);
+
+    puts(lockfile);
+
+    // signal parent to exit
+    kill(getppid(), SIGINT);
+
+    wait(&status);
+    int fd;
+    fd = openat(dirfd, lockfile, O_RDWR | O_APPEND);
+    fchmod(fd, 0600);
+    if (WIFEXITED(status))
+      dprintf(fd, "\n[exited with status %d.]\n", WEXITSTATUS(status));
+    else
+      dprintf(fd, "\n[killed by signal %d.]\n", WTERMSIG(status));
+
+    exit(0);
+  }
+
+  sprintf(lockfile, ".,%lx.%d", ms, getpid());
+  lockfd = openat(dirfd, lockfile, O_CREAT | O_EXCL | O_RDWR | O_APPEND, 0600);
+  
+  if (lockfd < 0) {
+    perror("open");
+    exit(222);
+  }
+
+  flock(lockfd, LOCK_EX);
+  
+  // drop leading .
+  renameat(dirfd, lockfile, dirfd, lockfile+1);
+
+  write(lockfd, "exec", 4);
+  int i;
+  for (i = 0; i < argc; i++) {
+      int j, l = strlen(argv[i]);
+      write(lockfd, " '", 2);
+      for (j = 0; j < l; j++) {
+        if (argv[i][j] == '\'')
+          write(lockfd, "'\\''", 4);
+        else
+          write(lockfd, argv[i]+j, 1);
+      }
+      write(lockfd, "'", 1);
+  }
+
+  DIR *dir = fdopendir(dirfd);
+  if (!dir) {
+    perror("fdopendir");
+    exit(111);
+  }
+
+  struct dirent *ent;
+
+again:
+  while ((ent = readdir(dir))) {
+    if (ent->d_name[0] == ',' && strcmp(ent->d_name, lockfile+1) < 0) {
+      int f = openat(dirfd, ent->d_name, O_RDWR);
+    
+      if (flock(f, LOCK_EX | LOCK_NB) == -1 && errno == EWOULDBLOCK) {
+        flock(f, LOCK_EX);   // sit it out
+
+        rewinddir(dir);
+        goto again;
+      }
+    
+      fchmod(f, 0600);
+      close(f);
+    }
+  }
+
+  closedir(dir);
+
+  // ready to run
+
+  write(lockfd, "\n", 1);
+
+  fchmod(lockfd, 0700);
+  
+  dup2(lockfd, 2);
+  dup2(lockfd, 1);
+  close(lockfd);
+  close(dirfd);
+
+  execvp(argv[1], argv+1);
+
+  return 111;
+}