IRC client
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

event.c 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /* Copyright (C) 2018 Curtis McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <assert.h>
  17. #include <err.h>
  18. #include <errno.h>
  19. #include <poll.h>
  20. #include <signal.h>
  21. #include <stdbool.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <stdnoreturn.h>
  25. #include <string.h>
  26. #include <sys/wait.h>
  27. #include <sysexits.h>
  28. #include <unistd.h>
  29. #include "chat.h"
  30. static struct {
  31. bool wait;
  32. int pipe;
  33. } child = {
  34. .pipe = -1,
  35. };
  36. void eventWait(const char *argv[static 2]) {
  37. uiHide();
  38. pid_t pid = fork();
  39. if (pid < 0) err(EX_OSERR, "fork");
  40. if (!pid) {
  41. execvp(argv[0], (char *const *)argv);
  42. err(EX_CONFIG, "%s", argv[0]);
  43. }
  44. child.wait = true;
  45. }
  46. static void childWait(void) {
  47. uiShow();
  48. int status;
  49. pid_t pid = wait(&status);
  50. if (pid < 0) err(EX_OSERR, "wait");
  51. if (WIFEXITED(status) && WEXITSTATUS(status)) {
  52. uiFmt(TagStatus, UIHot, "event: exit %d", WEXITSTATUS(status));
  53. } else if (WIFSIGNALED(status)) {
  54. uiFmt(
  55. TagStatus, UIHot,
  56. "event: signal %s", strsignal(WTERMSIG(status))
  57. );
  58. }
  59. child.wait = false;
  60. }
  61. void eventPipe(const char *argv[static 2]) {
  62. if (child.pipe > 0) {
  63. uiLog(TagStatus, UIHot, L"event: existing pipe");
  64. return;
  65. }
  66. int rw[2];
  67. int error = pipe(rw);
  68. if (error) err(EX_OSERR, "pipe");
  69. pid_t pid = fork();
  70. if (pid < 0) err(EX_OSERR, "fork");
  71. if (!pid) {
  72. close(rw[0]);
  73. close(STDIN_FILENO);
  74. dup2(rw[1], STDOUT_FILENO);
  75. dup2(rw[1], STDERR_FILENO);
  76. close(rw[1]);
  77. execvp(argv[0], (char *const *)argv);
  78. perror(argv[0]);
  79. exit(EX_CONFIG);
  80. }
  81. close(rw[1]);
  82. child.pipe = rw[0];
  83. }
  84. static void childRead(void) {
  85. char buf[256];
  86. ssize_t len = read(child.pipe, buf, sizeof(buf) - 1);
  87. if (len < 0) err(EX_IOERR, "read");
  88. if (len) {
  89. buf[len] = '\0';
  90. buf[strcspn(buf, "\n")] = '\0';
  91. uiFmt(TagStatus, UIHot, "event: %s", buf);
  92. } else {
  93. close(child.pipe);
  94. child.pipe = -1;
  95. }
  96. }
  97. static sig_atomic_t sig[NSIG];
  98. static void handler(int n) {
  99. sig[n] = 1;
  100. }
  101. noreturn void eventLoop(void) {
  102. sigset_t mask;
  103. sigemptyset(&mask);
  104. struct sigaction action = {
  105. .sa_handler = handler,
  106. .sa_mask = mask,
  107. .sa_flags = SA_RESTART | SA_NOCLDSTOP,
  108. };
  109. sigaction(SIGCHLD, &action, NULL);
  110. sigaction(SIGINT, &action, NULL);
  111. sigaction(SIGHUP, &action, NULL);
  112. struct sigaction curses;
  113. sigaction(SIGWINCH, &action, &curses);
  114. assert(!(curses.sa_flags & SA_SIGINFO));
  115. uiFmt(TagStatus, UICold, "Traveling to %s...", self.host);
  116. int irc = ircConnect();
  117. for (;;) {
  118. if (sig[SIGCHLD]) childWait();
  119. if (sig[SIGHUP]) {
  120. ircFmt("QUIT :zzz\r\n");
  121. self.quit = true;
  122. }
  123. if (sig[SIGINT]) {
  124. signal(SIGINT, SIG_DFL);
  125. ircFmt("QUIT :Goodbye\r\n");
  126. self.quit = true;
  127. }
  128. if (sig[SIGWINCH]) {
  129. curses.sa_handler(SIGWINCH);
  130. uiRead();
  131. uiDraw();
  132. }
  133. sig[SIGCHLD] = sig[SIGHUP] = sig[SIGINT] = sig[SIGWINCH] = 0;
  134. struct pollfd fds[3] = {
  135. { .events = POLLIN, .fd = irc },
  136. { .events = POLLIN, .fd = STDIN_FILENO },
  137. { .events = POLLIN, .fd = child.pipe },
  138. };
  139. if (child.wait) fds[1].events = 0;
  140. if (child.pipe < 0) fds[2].events = 0;
  141. int nfds = poll(fds, 3, -1);
  142. if (nfds < 0) {
  143. if (errno == EINTR) continue;
  144. err(EX_IOERR, "poll");
  145. }
  146. if (fds[0].revents) ircRead();
  147. if (fds[1].revents) uiRead();
  148. if (fds[2].revents) childRead();
  149. uiDraw();
  150. }
  151. }