main.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/socket.h>
  5. #include <sys/un.h>
  6. #include <sys/wait.h>
  7. #include <sys/prctl.h>
  8. #include "util.h"
  9. #include "postprocess.h"
  10. static char socket_path[100];
  11. struct Job {
  12. pid_t pid;
  13. char burstdir[255];
  14. char target[255];
  15. int keep;
  16. };
  17. void
  18. start_processing(struct Job job)
  19. {
  20. postprocess_internal(job.burstdir, job.target, 1);
  21. }
  22. int
  23. is_daemon_running()
  24. {
  25. int sock;
  26. struct sockaddr_un addr;
  27. struct timeval tv;
  28. // Daemon isn't running if the socket doesn't exist
  29. if (!access(socket_path, F_OK)) {
  30. return 0;
  31. }
  32. // Check if the daemon responds on the socket
  33. sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
  34. if (sock < 0) {
  35. err("could not make socket fd");
  36. return 0;
  37. }
  38. // Set a short timeout on the socket
  39. tv.tv_sec = 0;
  40. tv.tv_usec = 500;
  41. setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv);
  42. memset(&addr, 0, sizeof(struct sockaddr_un));
  43. addr.sun_family = AF_UNIX;
  44. strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
  45. if (connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) {
  46. return 0;
  47. }
  48. close(sock);
  49. fprintf(stderr, "[fg] daemon is already running\n");
  50. return 1;
  51. }
  52. int
  53. listen_on_socket()
  54. {
  55. int sock;
  56. struct sockaddr_un addr;
  57. // Clean existing socket
  58. if (remove(socket_path) == -1 && errno != ENOENT) {
  59. err("could not clean up old socket");
  60. }
  61. // Make new unix domain socket to listen on
  62. sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
  63. if (sock < 0) {
  64. err("could not make socket fd");
  65. return 0;
  66. }
  67. memset(&addr, 0, sizeof(struct sockaddr_un));
  68. addr.sun_family = AF_UNIX;
  69. strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
  70. unlink(socket_path);
  71. if (bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) {
  72. err("failed to bind socket");
  73. return 0;
  74. }
  75. if (listen(sock, 20) < 0) {
  76. err("failed to listen");
  77. return 0;
  78. }
  79. return sock;
  80. }
  81. int
  82. queue_job(struct Job job)
  83. {
  84. int sock;
  85. struct sockaddr_un addr;
  86. char buffer[1024];
  87. sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
  88. if (sock < 0) {
  89. err("[fg] could not make socket fd");
  90. return 1;
  91. }
  92. memset(&addr, 0, sizeof(struct sockaddr_un));
  93. addr.sun_family = AF_UNIX;
  94. strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
  95. if (connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) < 0) {
  96. err("[fg] failed to open socket");
  97. return 2;
  98. }
  99. if (write(sock, &job, sizeof(job)) < 0) {
  100. err("[fg] failed to write");
  101. return 3;
  102. }
  103. fprintf(stderr, "[fg] wait until processing is done\n");
  104. // Wait until the background process return the resulting filename
  105. if (read(sock, buffer, 1024) < 1) {
  106. err("[fg] failed to read");
  107. return 4;
  108. }
  109. fprintf(stderr, "[fg] processing is done\n");
  110. // Pass the stacked filename to megapixels
  111. printf("%s\n", buffer);
  112. fprintf(stderr, "[fg] done\n");
  113. exit(0);
  114. }
  115. int
  116. start_background_process()
  117. {
  118. int sock, fd;
  119. struct sockaddr_un cli_addr;
  120. unsigned int cli_len;
  121. struct Job job;
  122. char buffer[272];
  123. const char *name_fg = "postprocessd fg";
  124. const char *name_bg = "postprocessd bg";
  125. // First fork
  126. pid_t child_pid = fork();
  127. if (child_pid < 0) {
  128. err("fork failed");
  129. } else if (child_pid > 0) {
  130. prctl(PR_SET_NAME, (unsigned long) name_fg);
  131. // In the parent process
  132. waitpid(child_pid, NULL, WNOHANG);
  133. // Give the fork a bit of time to create the socket
  134. while (access(socket_path, F_OK)) {
  135. usleep(100);
  136. }
  137. usleep(1000);
  138. return 1;
  139. }
  140. // Create new process group
  141. setsid();
  142. // Second fork
  143. pid_t child2_pid = fork();
  144. if (child2_pid != 0) {
  145. // The middle child, exit quickly
  146. exit(0);
  147. }
  148. // We're now in the grandchild
  149. prctl(PR_SET_NAME, (unsigned long) name_bg);
  150. // Clean up FDs
  151. for (fd = sysconf(_SC_OPEN_MAX); fd > 0; --fd) {
  152. close(fd);
  153. }
  154. // Recreate standard pipes
  155. #ifdef __GLIBC__
  156. stdin = fopen("/dev/null", "r");
  157. stdout = fopen("/dev/null", "w+");
  158. stderr = fopen("/dev/null", "w+");
  159. #else
  160. freopen("/dev/null", "r", stdin);
  161. freopen("/dev/null", "w", stdout);
  162. freopen("/dev/null", "w", stderr);
  163. #endif
  164. cli_len = sizeof(cli_addr);
  165. fprintf(stderr, "[bg] init postprocessd\n");
  166. postprocess_setup();
  167. sock = listen_on_socket();
  168. fprintf(stderr, "[bg] socket created\n");
  169. while (1) {
  170. fd = accept(sock, (struct sockaddr *) &cli_addr, &cli_len);
  171. if (fd < 0) {
  172. err("[bg] failed to accept");
  173. return 0;
  174. }
  175. fprintf(stderr, "[bg] accepted connection\n");
  176. if (read(fd, &job, sizeof(job)) < 0) {
  177. err("[bg] failed to read");
  178. return 0;
  179. }
  180. fprintf(stderr, "[bg] start processing job\n");
  181. start_processing(job);
  182. wait(NULL);
  183. fprintf(stderr, "[bg] job done\n");
  184. snprintf(buffer, sizeof(buffer), "%s.stacked.jpg", job.target);
  185. fprintf(stderr, "[bg] result: '%s'\n", buffer);
  186. if (write(fd, buffer, sizeof(buffer)) < 0) {
  187. err("[bg] failed to write response");
  188. }
  189. close(fd);
  190. }
  191. }
  192. void
  193. make_socket_path()
  194. {
  195. char fname[80];
  196. char *xdg_runtime_dir = getenv("XDG_RUNTIME_DIR");
  197. char *user = getenv("USER");
  198. snprintf(fname, sizeof(fname), "postprocessd-%s.sock", user);
  199. if (xdg_runtime_dir) {
  200. snprintf(socket_path, sizeof(socket_path), "%s/%s", xdg_runtime_dir, fname);
  201. } else {
  202. snprintf(socket_path, sizeof(socket_path), "/tmp/%s", fname);
  203. }
  204. fprintf(stderr, "[fg] using socket '%s'\n", socket_path);
  205. }
  206. int
  207. handle_job(struct Job job)
  208. {
  209. /*
  210. * There's two parts to postprocessd, the postprocessd binary is called
  211. * by Megapixels and will check if there's already a daemon running.
  212. * If there isn't then it will fork() a daemon process that opens a socket.
  213. *
  214. * The main process will connect to that process over the unix socket and
  215. * send the task. Then it'll wait until the background process is completed
  216. * so the filename can be returned to Megapixels. This also means that if
  217. * multiple pictures are taken while processing there will be a few
  218. * postprocessd processes running that only wait on the daemon without using
  219. * CPU and the the daemon will process all the tasks SEQUENTIALLY until
  220. * done and notify the right waiting processes the job is done.
  221. */
  222. if (!is_daemon_running()) {
  223. fprintf(stderr, "[fg] starting new daemon\n");
  224. unlink(socket_path);
  225. start_background_process();
  226. }
  227. fprintf(stderr, "[fg] queueing job\n");
  228. return queue_job(job);
  229. }
  230. int
  231. main(int argc, char *argv[])
  232. {
  233. struct Job job;
  234. make_socket_path();
  235. if (argc == 4) {
  236. // Parse command line arguments into the job struct
  237. job.pid = 0;
  238. strncpy(job.burstdir, argv[1], sizeof(job.burstdir));
  239. strncpy(job.target, argv[2], sizeof(job.target));
  240. if (strcmp(argv[3], "0") == 0) {
  241. job.keep = 0;
  242. } else {
  243. job.keep = 1;
  244. }
  245. return handle_job(job);
  246. } else {
  247. printf("usage: %s burst-dir target-name keep\n", argv[0]);
  248. exit(1);
  249. }
  250. }