main.c 7.4 KB

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