|
@@ -4,189 +4,286 @@
|
|
#include <sys/socket.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/wait.h>
|
|
-
|
|
|
|
|
|
+#include <sys/prctl.h>
|
|
#include "util.h"
|
|
#include "util.h"
|
|
#include "postprocess.h"
|
|
#include "postprocess.h"
|
|
|
|
|
|
static char socket_path[100];
|
|
static char socket_path[100];
|
|
|
|
|
|
struct Job {
|
|
struct Job {
|
|
- pid_t pid;
|
|
|
|
- char burstdir[255];
|
|
|
|
- char target[255];
|
|
|
|
- int keep;
|
|
|
|
|
|
+ pid_t pid;
|
|
|
|
+ char burstdir[255];
|
|
|
|
+ char target[255];
|
|
|
|
+ int keep;
|
|
};
|
|
};
|
|
|
|
|
|
-
|
|
|
|
-pid_t
|
|
|
|
|
|
+void
|
|
start_processing(struct Job job)
|
|
start_processing(struct Job job)
|
|
{
|
|
{
|
|
- pid_t child_pid = fork();
|
|
|
|
- if (child_pid < 0) {
|
|
|
|
- err("fork failed");
|
|
|
|
- } else if (child_pid > 0) {
|
|
|
|
- // parent process
|
|
|
|
- return child_pid;
|
|
|
|
- } else {
|
|
|
|
- // child process
|
|
|
|
- postprocess_internal(job.burstdir, job.target, 1);
|
|
|
|
- exit(0);
|
|
|
|
- }
|
|
|
|
- return -1;
|
|
|
|
|
|
+ postprocess_internal(job.burstdir, job.target, 1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+is_daemon_running()
|
|
|
|
+{
|
|
|
|
+ int sock;
|
|
|
|
+ struct sockaddr_un addr;
|
|
|
|
+ struct timeval tv;
|
|
|
|
+
|
|
|
|
+ // Daemon isn't running if the socket doesn't exist
|
|
|
|
+ if (!access(socket_path, F_OK)) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Check if the daemon responds on the socket
|
|
|
|
+ sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
|
|
|
+ if (sock < 0) {
|
|
|
|
+ err("could not make socket fd");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Set a short timeout on the socket
|
|
|
|
+ tv.tv_sec = 0;
|
|
|
|
+ tv.tv_usec = 500;
|
|
|
|
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv);
|
|
|
|
+
|
|
|
|
+ memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
|
|
+ addr.sun_family = AF_UNIX;
|
|
|
|
+ strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
|
|
|
+ if (connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ close(sock);
|
|
|
|
+ fprintf(stderr, "[fg] daemon is already running\n");
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
int
|
|
listen_on_socket()
|
|
listen_on_socket()
|
|
{
|
|
{
|
|
- int sock;
|
|
|
|
- struct sockaddr_un addr;
|
|
|
|
-
|
|
|
|
- // Clean existing socket
|
|
|
|
- if (remove(socket_path) == -1 && errno != ENOENT) {
|
|
|
|
- err("could not clean up old socket");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Make new unix domain socket to listen on
|
|
|
|
- sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
|
|
|
- if (sock < 0) {
|
|
|
|
- err("could not make socket fd");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
|
|
- addr.sun_family = AF_UNIX;
|
|
|
|
- strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
|
|
|
- if (bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) {
|
|
|
|
- err("failed to bind socket");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (listen(sock, 20) < 0) {
|
|
|
|
- err("failed to listen");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return sock;
|
|
|
|
|
|
+ int sock;
|
|
|
|
+ struct sockaddr_un addr;
|
|
|
|
+
|
|
|
|
+ // Clean existing socket
|
|
|
|
+ if (remove(socket_path) == -1 && errno != ENOENT) {
|
|
|
|
+ err("could not clean up old socket");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Make new unix domain socket to listen on
|
|
|
|
+ sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
|
|
|
+ if (sock < 0) {
|
|
|
|
+ err("could not make socket fd");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
|
|
+ addr.sun_family = AF_UNIX;
|
|
|
|
+ strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
|
|
|
+ unlink(socket_path);
|
|
|
|
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) {
|
|
|
|
+ err("failed to bind socket");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (listen(sock, 20) < 0) {
|
|
|
|
+ err("failed to listen");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return sock;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
int
|
|
queue_job(struct Job job)
|
|
queue_job(struct Job job)
|
|
{
|
|
{
|
|
- int sock;
|
|
|
|
- struct sockaddr_un addr;
|
|
|
|
-
|
|
|
|
- sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
|
|
|
- if (sock < 0) {
|
|
|
|
- err("could not make socket fd");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
|
|
- addr.sun_family = AF_UNIX;
|
|
|
|
- strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
|
|
|
- if (connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) {
|
|
|
|
- err("failed to open socket");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- if (write(sock, &job, sizeof(job)) < 0) {
|
|
|
|
- err("failed to write");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- close(sock);
|
|
|
|
- return 1;
|
|
|
|
|
|
+ int sock;
|
|
|
|
+ struct sockaddr_un addr;
|
|
|
|
+ char buffer[1024];
|
|
|
|
+
|
|
|
|
+ sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
|
|
|
+ if (sock < 0) {
|
|
|
|
+ err("[fg] could not make socket fd");
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
|
|
+ addr.sun_family = AF_UNIX;
|
|
|
|
+ strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
|
|
|
+ if (connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) {
|
|
|
|
+ err("[fg] failed to open socket");
|
|
|
|
+ return 2;
|
|
|
|
+ }
|
|
|
|
+ if (write(sock, &job, sizeof(job)) < 0) {
|
|
|
|
+ err("[fg] failed to write");
|
|
|
|
+ return 3;
|
|
|
|
+ }
|
|
|
|
+ fprintf(stderr, "[fg] wait until processing is done\n");
|
|
|
|
+ // Wait until the background process return the resulting filename
|
|
|
|
+ if (read(sock, buffer, 1024) < 1) {
|
|
|
|
+ err("[fg] failed to read");
|
|
|
|
+ return 4;
|
|
|
|
+ }
|
|
|
|
+ fprintf(stderr, "[fg] processing is done\n");
|
|
|
|
+
|
|
|
|
+ // Pass the stacked filename to megapixels
|
|
|
|
+ printf("%s\n", buffer);
|
|
|
|
+ fprintf(stderr, "[fg] done\n");
|
|
|
|
+ exit(0);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
int
|
|
-listen_and_convert(int do_fork)
|
|
|
|
|
|
+start_background_process()
|
|
{
|
|
{
|
|
- int sock, fd;
|
|
|
|
- struct sockaddr_un cli_addr;
|
|
|
|
- unsigned int clilen;
|
|
|
|
- struct Job job;
|
|
|
|
-
|
|
|
|
- if (do_fork == 1) {
|
|
|
|
- pid_t child_pid = fork();
|
|
|
|
- if (child_pid < 0) {
|
|
|
|
- err("fork failed");
|
|
|
|
- } else if (child_pid > 0) {
|
|
|
|
- usleep(1000000);
|
|
|
|
- // parent process
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- clilen = sizeof(cli_addr);
|
|
|
|
-
|
|
|
|
- postprocess_setup();
|
|
|
|
- sock = listen_on_socket();
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- while(1) {
|
|
|
|
- fd = accept(sock, (struct sockaddr *)&cli_addr, &clilen);
|
|
|
|
- if (fd < 0) {
|
|
|
|
- err("failed to accept");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- if(read(fd, &job, sizeof(job)) < 0) {
|
|
|
|
- err("failed to read");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- close(fd);
|
|
|
|
-
|
|
|
|
- start_processing(job);
|
|
|
|
- wait(NULL);
|
|
|
|
- }
|
|
|
|
|
|
+ int sock, fd;
|
|
|
|
+ struct sockaddr_un cli_addr;
|
|
|
|
+ unsigned int cli_len;
|
|
|
|
+ struct Job job;
|
|
|
|
+ char buffer[272];
|
|
|
|
+
|
|
|
|
+ const char *name_fg = "postprocessd fg";
|
|
|
|
+ const char *name_bg = "postprocessd bg";
|
|
|
|
+
|
|
|
|
+ // First fork
|
|
|
|
+ pid_t child_pid = fork();
|
|
|
|
+ if (child_pid < 0) {
|
|
|
|
+ err("fork failed");
|
|
|
|
+ } else if (child_pid > 0) {
|
|
|
|
+ prctl(PR_SET_NAME, (unsigned long) name_fg);
|
|
|
|
+ // In the parent process
|
|
|
|
+ waitpid(child_pid, NULL, WNOHANG);
|
|
|
|
+
|
|
|
|
+ // Give the fork a bit of time to create the socket
|
|
|
|
+ while (access(socket_path, F_OK)) {
|
|
|
|
+ usleep(100);
|
|
|
|
+ }
|
|
|
|
+ usleep(1000);
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Create new process group
|
|
|
|
+ setsid();
|
|
|
|
+
|
|
|
|
+ // Second fork
|
|
|
|
+ pid_t child2_pid = fork();
|
|
|
|
+ if (child2_pid != 0) {
|
|
|
|
+ // The middle child, exit quickly
|
|
|
|
+ exit(0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // We're now in the grandchild
|
|
|
|
+ prctl(PR_SET_NAME, (unsigned long) name_bg);
|
|
|
|
+
|
|
|
|
+ // Clean up FDs
|
|
|
|
+ for (fd = sysconf(_SC_OPEN_MAX); fd > 0; --fd) {
|
|
|
|
+ close(fd);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Recreate standard pipes
|
|
|
|
+#ifdef __GLIBC__
|
|
|
|
+ stdin = fopen("/dev/null", "r");
|
|
|
|
+ stdout = fopen("/dev/null", "w+");
|
|
|
|
+ stderr = fopen("/dev/null", "w+");
|
|
|
|
+#else
|
|
|
|
+ freopen("/dev/null", "r", stdin);
|
|
|
|
+ freopen("/dev/null", "w", stdout);
|
|
|
|
+ freopen("/dev/null", "w", stderr);
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ cli_len = sizeof(cli_addr);
|
|
|
|
+
|
|
|
|
+ fprintf(stderr, "[bg] init postprocessd\n");
|
|
|
|
+ postprocess_setup();
|
|
|
|
+ sock = listen_on_socket();
|
|
|
|
+ fprintf(stderr, "[bg] socket created\n");
|
|
|
|
+
|
|
|
|
+ while (1) {
|
|
|
|
+ fd = accept(sock, (struct sockaddr *) &cli_addr, &cli_len);
|
|
|
|
+ if (fd < 0) {
|
|
|
|
+ err("[bg] failed to accept");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ fprintf(stderr, "[bg] accepted connection\n");
|
|
|
|
+ if (read(fd, &job, sizeof(job)) < 0) {
|
|
|
|
+ err("[bg] failed to read");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ fprintf(stderr, "[bg] start processing job\n");
|
|
|
|
+ start_processing(job);
|
|
|
|
+ wait(NULL);
|
|
|
|
+ fprintf(stderr, "[bg] job done\n");
|
|
|
|
+
|
|
|
|
+ snprintf(buffer, sizeof(buffer), "%s.stacked.jpg", job.target);
|
|
|
|
+ fprintf(stderr, "[bg] result: '%s'\n", buffer);
|
|
|
|
+ if (write(fd, buffer, sizeof(buffer)) < 0) {
|
|
|
|
+ err("[bg] failed to write response");
|
|
|
|
+ }
|
|
|
|
+ close(fd);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
void
|
|
make_socket_path()
|
|
make_socket_path()
|
|
{
|
|
{
|
|
- char fname[80];
|
|
|
|
- char *xdg_runtime_dir = getenv("XDG_RUNTIME_DIR");
|
|
|
|
- char *user = getenv("USER");
|
|
|
|
|
|
+ char fname[80];
|
|
|
|
+ char *xdg_runtime_dir = getenv("XDG_RUNTIME_DIR");
|
|
|
|
+ char *user = getenv("USER");
|
|
|
|
|
|
- snprintf(fname, sizeof(fname), "postprocessd-%s.sock", user);
|
|
|
|
|
|
+ snprintf(fname, sizeof(fname), "postprocessd-%s.sock", user);
|
|
|
|
|
|
- if (xdg_runtime_dir) {
|
|
|
|
- snprintf(socket_path, sizeof(socket_path), "%s/%s", xdg_runtime_dir, fname);
|
|
|
|
- } else {
|
|
|
|
- snprintf(socket_path, sizeof(socket_path), "/tmp/%s", fname);
|
|
|
|
- }
|
|
|
|
|
|
+ if (xdg_runtime_dir) {
|
|
|
|
+ snprintf(socket_path, sizeof(socket_path), "%s/%s", xdg_runtime_dir, fname);
|
|
|
|
+ } else {
|
|
|
|
+ snprintf(socket_path, sizeof(socket_path), "/tmp/%s", fname);
|
|
|
|
+ }
|
|
|
|
+ fprintf(stderr, "[fg] using socket '%s'\n", socket_path);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+handle_job(struct Job job)
|
|
|
|
+{
|
|
|
|
+ /*
|
|
|
|
+ * There's two parts to postprocessd, the postprocessd binary is called
|
|
|
|
+ * by Megapixels and will check if there's already a daemon running.
|
|
|
|
+ * If there isn't then it will fork() a daemon process that opens a socket.
|
|
|
|
+ *
|
|
|
|
+ * The main process will connect to that process over the unix socket and
|
|
|
|
+ * send the task. Then it'll wait until the background process is completed
|
|
|
|
+ * so the filename can be returned to Megapixels. This also means that if
|
|
|
|
+ * multiple pictures are taken while processing there will be a few
|
|
|
|
+ * postprocessd processes running that only wait on the daemon without using
|
|
|
|
+ * CPU and the the daemon will process all the tasks SEQUENTIALLY until
|
|
|
|
+ * done and notify the right waiting processes the job is done.
|
|
|
|
+ */
|
|
|
|
+ if (!is_daemon_running()) {
|
|
|
|
+ fprintf(stderr, "[fg] starting new daemon\n");
|
|
|
|
+ unlink(socket_path);
|
|
|
|
+ start_background_process();
|
|
|
|
+ }
|
|
|
|
+ fprintf(stderr, "[fg] queueing job\n");
|
|
|
|
+ return queue_job(job);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
int
|
|
main(int argc, char *argv[])
|
|
main(int argc, char *argv[])
|
|
{
|
|
{
|
|
- struct Job job;
|
|
|
|
- make_socket_path();
|
|
|
|
-
|
|
|
|
- if (argc == 4) {
|
|
|
|
- // Parse command line arguments into the job struct
|
|
|
|
- job.pid = 0;
|
|
|
|
- strncpy(job.burstdir, argv[1], sizeof(job.burstdir));
|
|
|
|
- strncpy(job.target, argv[2], sizeof(job.target));
|
|
|
|
- if (strcmp(argv[3], "0") == 0) {
|
|
|
|
- job.keep = 0;
|
|
|
|
- } else {
|
|
|
|
- job.keep = 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if(queue_job(job)) return 0;
|
|
|
|
-
|
|
|
|
- if(listen_and_convert(1))
|
|
|
|
- queue_job(job);
|
|
|
|
- } else if (argc == 2) {
|
|
|
|
- if (strcmp(argv[1], "--daemon") == 0) {
|
|
|
|
- listen_and_convert(0);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- printf("usage: %s burst-dir target-name keep\n", argv[0]);
|
|
|
|
- exit(1);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if(listen_and_convert(1))
|
|
|
|
- queue_job(job);
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
|
|
+ struct Job job;
|
|
|
|
+ make_socket_path();
|
|
|
|
+
|
|
|
|
+ if (argc == 4) {
|
|
|
|
+ // Parse command line arguments into the job struct
|
|
|
|
+ job.pid = 0;
|
|
|
|
+ strncpy(job.burstdir, argv[1], sizeof(job.burstdir));
|
|
|
|
+ strncpy(job.target, argv[2], sizeof(job.target));
|
|
|
|
+ if (strcmp(argv[3], "0") == 0) {
|
|
|
|
+ job.keep = 0;
|
|
|
|
+ } else {
|
|
|
|
+ job.keep = 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return handle_job(job);
|
|
|
|
+ } else {
|
|
|
|
+ printf("usage: %s burst-dir target-name keep\n", argv[0]);
|
|
|
|
+ exit(1);
|
|
|
|
+ }
|
|
}
|
|
}
|