University stuff.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. #include "job.h"
  2. #include "tcp.h"
  3. #include "argparser.h"
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <stdint.h>
  7. #include <string.h>
  8. #include <errno.h>
  9. #include <unistd.h>
  10. #include <netdb.h>
  11. #include <signal.h>
  12. #ifndef NOREADLINE
  13. #include <readline/readline.h>
  14. #include <readline/history.h>
  15. #endif
  16. #define BUF_SIZE 5
  17. static int sockfd = -1;
  18. pid_t pid_a;
  19. pid_t pid_b;
  20. static int pipe_a[2];
  21. static int pipe_b[2];
  22. // input_str will be a pointer to the string containing user input,
  23. // or NULL if there's no user input to be freed.
  24. static char *input_str = NULL;
  25. /*
  26. * Terminate the program, closing sockets and sending close messages.
  27. *
  28. * Input:
  29. * code: The exit code.
  30. * err: The string to send to perror.
  31. * If NULL, perror won't be called.
  32. */
  33. void term(int code, char *err)
  34. {
  35. if (err != NULL)
  36. {
  37. perror(err);
  38. }
  39. if (sockfd != -1)
  40. {
  41. char buf[BUF_SIZE];
  42. memset(buf, 0, sizeof(buf));
  43. if (code == 0)
  44. buf[0] = 'T';
  45. else
  46. buf[0] = 'E';
  47. write(sockfd, buf, sizeof(buf));
  48. close(sockfd);
  49. }
  50. free(input_str);
  51. kill(pid_a, SIGTERM);
  52. kill(pid_b, SIGTERM);
  53. close(pipe_a[0]);
  54. close(pipe_a[1]);
  55. close(pipe_b[0]);
  56. close(pipe_b[1]);
  57. #ifndef NOREADLINE
  58. clear_history();
  59. #endif
  60. exit(code);
  61. }
  62. /*
  63. * Like term, but with herror instead of perror.
  64. */
  65. void hterm(int code, char *err)
  66. {
  67. if (err != NULL)
  68. {
  69. herror(err);
  70. }
  71. term(code, NULL);
  72. }
  73. /*
  74. * Help text
  75. */
  76. void cmd_help()
  77. {
  78. printf("\thelp:\n\t\tShow this help text.\n\n");
  79. printf("\tget [number | all]:\n\t\tGet [number] jobs, defaults to 1.\n\n");
  80. printf("\tq:\n\t\nExit.\n");
  81. }
  82. /*
  83. * Get jobs and send them to a child process.
  84. *
  85. * Input:
  86. * count:
  87. * if NULL: get 1 job
  88. * if number: get <number> jobs
  89. * if "all": get all jobs
  90. */
  91. void cmd_get(char *count)
  92. {
  93. uint32_t c = 1;
  94. if (count != NULL && strcmp(count, "all") == 0)
  95. c = ~((uint32_t)0);
  96. else if (count != NULL)
  97. c = atoi(count);
  98. char buf[BUF_SIZE];
  99. memset(buf, 0, 5);
  100. buf[0] = 'G';
  101. buf[1] = c;
  102. buf[2] = c >> 8;
  103. buf[3] = c >> 16;
  104. buf[4] = c >> 24;
  105. if (write(sockfd, buf, sizeof(buf)) < 0)
  106. term(1, "Couldn't send data");
  107. char res[sizeof(job)];
  108. for (int i = 0; i < c; ++i)
  109. {
  110. memset(res, 0, sizeof(res));
  111. if (read(sockfd, res, sizeof(res)) < 0)
  112. term(1, "Couldn't read data");
  113. int written = 0;
  114. // Send to child A if O or Q
  115. if (res[0] == 'O' || res[0] == 'Q')
  116. {
  117. written = 1;
  118. if (write(pipe_a[1], res, sizeof(job)) < 0)
  119. term(1, "Couldn't write to pipe");
  120. }
  121. // Send to child B if E or Q
  122. if (res[0] == 'E' || res[0] == 'Q')
  123. {
  124. written = 1;
  125. if (write(pipe_b[1], res, sizeof(job)) < 0)
  126. term(1, "Couldn't write to pipe");
  127. }
  128. // Quit if Q
  129. if (res[0] == 'Q')
  130. {
  131. usleep(50 * 1000);
  132. // Children should have exited by now
  133. term(0, NULL);
  134. }
  135. // Server died if 0
  136. if (res[0] == 0)
  137. {
  138. fprintf(stderr, "Server disconnected!\n");
  139. term(1, NULL);
  140. }
  141. // Error if we received an invalid type
  142. if (!written)
  143. {
  144. fprintf(stderr, "Received invalid job type: %c\n", res[0]);
  145. term(1, NULL);
  146. }
  147. }
  148. }
  149. /*
  150. * Exit gracefully.
  151. */
  152. void cmd_exit()
  153. {
  154. term(0, NULL);
  155. }
  156. /*
  157. * Evaluate an input string, do the desired action.
  158. *
  159. * Input:
  160. * str: The input string.
  161. */
  162. int eval(char *str)
  163. {
  164. // Parse input, we won't need more than 2 arguments
  165. char *argv[2];
  166. argparser_parse(argv, 2, str);
  167. char *cmd = argv[0];
  168. if (argv[0] == NULL)
  169. return 0;
  170. if (strcmp(cmd, "help") == 0)
  171. cmd_help();
  172. else if (strcmp(cmd, "get") == 0)
  173. cmd_get(argv[1]);
  174. else if (strcmp(cmd, "q") == 0)
  175. cmd_exit(str);
  176. else
  177. printf("Unknown command: '%s'. Type 'help' for help.\n", cmd);
  178. return 1;
  179. }
  180. /*
  181. * SIGINT/SIGTERM handler
  182. */
  183. void onterm(int signum)
  184. {
  185. printf("\n");
  186. term(1, NULL);
  187. }
  188. // Code shared between both the children.
  189. //
  190. // Input:
  191. // fd: The file descriptor to read from.
  192. // out: the FILE* to write to.
  193. void child_shared(int fd, FILE *out)
  194. {
  195. char buf[sizeof(job)];
  196. job j;
  197. while (1)
  198. {
  199. if (read(fd, buf, sizeof(buf)) < 0)
  200. term(1, "Couldn't read from pipe");
  201. job_parse(buf, &j);
  202. if (j.type == 'Q')
  203. {
  204. exit(0);
  205. }
  206. else
  207. {
  208. fprintf(out, "%s\n", j.text);
  209. fflush(out);
  210. }
  211. }
  212. }
  213. void child_a()
  214. {
  215. child_shared(pipe_a[0], stdout);
  216. }
  217. void child_b()
  218. {
  219. child_shared(pipe_b[0], stderr);
  220. }
  221. int main(int argc, char *argv[])
  222. {
  223. if (argc < 2)
  224. {
  225. printf("Usage: %s <host> <port>\n", argv[0]);
  226. return 1;
  227. }
  228. // Create child A
  229. if (pipe(pipe_a) < 0)
  230. term(1, "Couldn't create pipe");
  231. pid_a = fork();
  232. if (pid_a < 0)
  233. term(1, "Fork failed");
  234. else if (pid_a == 0)
  235. child_a();
  236. // Create child B
  237. if (pipe(pipe_b) < 0)
  238. term(1, "Couldn't create pipe");
  239. pid_b = fork();
  240. if (pid_b < 0)
  241. term(1, "Fork failed");
  242. else if (pid_b == 0)
  243. child_b();
  244. // SIGTERM an SIGINT handler
  245. struct sigaction action;
  246. memset(&action, 0, sizeof(action));
  247. action.sa_handler = onterm;
  248. sigaction(SIGTERM, &action, NULL);
  249. sigaction(SIGINT, &action, NULL);
  250. char *host = argv[1];
  251. int port = atoi(argv[2]);
  252. // Create socket
  253. sockfd = tcp_create_sock(host, port);
  254. if (sockfd == -1)
  255. term(1, "Couldn't create socket");
  256. else if (sockfd == -2)
  257. hterm(1, "Couldn't create socket");
  258. printf("Connected to server on %s:%i\n", host, port);
  259. printf("Type 'help' for help.\n");
  260. // Read user input from readline
  261. while (1)
  262. {
  263. #ifndef NOREADLINE
  264. input_str = readline("> ");
  265. if (!input_str)
  266. {
  267. fprintf(stderr, "Failed to read user input.\n");
  268. term(1, NULL);
  269. }
  270. add_history(input_str);
  271. #else
  272. input_str = NULL;
  273. size_t n = 0;
  274. if (getline(&input_str, &n, stdin) < 0)
  275. {
  276. free(input_str);
  277. fprintf(stderr, "Failed to read user input.\n");
  278. term(1, NULL);
  279. }
  280. #endif
  281. eval(input_str);
  282. free(input_str);
  283. input_str = NULL;
  284. // There's a race condition; the child processes might print
  285. // their output after we print the "> " prompt, which isn't good.
  286. // This is an imperfect fix.
  287. usleep(100 * 1000);
  288. }
  289. return 0;
  290. }