TFBReaper.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #define _DEFAULT_SOURCE
  2. #include <signal.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>
  6. #include <sys/wait.h>
  7. #include <sys/prctl.h>
  8. #include <string.h>
  9. typedef struct Node Node;
  10. /**
  11. * Simple linked-list struct.
  12. */
  13. struct Node
  14. {
  15. char *str;
  16. Node *next;
  17. };
  18. /**
  19. * References to the head and tail of the linked-list.
  20. */
  21. Node *head = NULL;
  22. Node *tail = NULL;
  23. /**
  24. * Reap will recursively find all processes with this process
  25. * as an ancestor, and kill them.
  26. */
  27. void reap(int signum)
  28. {
  29. int pid = getpid();
  30. FILE *fp;
  31. char buf[256];
  32. char command[256];
  33. sprintf(command, "findChilds() { for child in $(ps --ppid $1 ho pid); do echo $child; findChilds $child; done } && findChilds %d", pid);
  34. int count;
  35. do
  36. {
  37. count = 0;
  38. char *pids[256];
  39. fp = popen(command, "r");
  40. while(fgets(buf, sizeof(buf), fp) != 0)
  41. {
  42. Node *newNode = malloc(sizeof(Node));
  43. newNode->str = malloc(strlen(buf)+1);
  44. strcpy(newNode->str, buf);
  45. newNode->next = NULL;
  46. if(tail == NULL)
  47. {
  48. tail = newNode;
  49. head = newNode;
  50. }
  51. else
  52. {
  53. if(head->next == NULL)
  54. {
  55. head->next = newNode;
  56. }
  57. tail->next = newNode;
  58. tail = newNode;
  59. }
  60. count ++;
  61. }
  62. Node *curr = head;
  63. while(curr != NULL)
  64. {
  65. kill(atoi(curr->str), SIGKILL);
  66. waitpid(atoi(curr->str), NULL, 0);
  67. curr = curr->next;
  68. }
  69. }
  70. // This may seem magical, but that command from above always results in two
  71. // additionally PIDs: one for `ps` and one for `sh`. Therefore, all of the
  72. // lineage of this TFBReaper have been successfully killed once there are
  73. // only two PIDs counted in the loop.
  74. // This loop is necessary for edge cases where there is a master->slave
  75. // lineage and TFBReaper kills a slave first, which is observed and fixed
  76. // by the master by spawning a NEW slave in the original's place, and then
  77. // killing the master (thus orphaning the newly spawned slave, but that PID
  78. // is not in our master list).
  79. while(count > 2);
  80. exit(0);
  81. }
  82. int main(int argc, char *argv[])
  83. {
  84. // Interrupt SIGTERM and SIGINT and pass to our handler.
  85. struct sigaction action;
  86. memset(&action, 0, sizeof(action));
  87. action.sa_handler = reap;
  88. sigaction(SIGTERM, &action, NULL);
  89. sigaction(SIGINT, &action, NULL);
  90. // Gather the command line arguments for the pass-through.
  91. int count = argc - 1;
  92. int *sizes = malloc(sizeof(int) * count);
  93. int total_size = 0;
  94. for( int i = 1; i < argc; i++ ) {
  95. sizes[i - 1] = strlen(argv[i]);
  96. total_size += sizes[i - 1];
  97. }
  98. char *result = malloc(sizeof(char) * total_size + count);
  99. char *ptr = result;
  100. for( int i = 1; i < argc; i++ ) {
  101. memcpy(ptr, argv[i], sizes[i - 1]);
  102. ptr[sizes[i - 1]] = ' ';
  103. ptr += sizes[i - 1] + 1;
  104. }
  105. *ptr = '\0';
  106. free(sizes);
  107. // Here is the magic. This sets any child processes to
  108. // use THIS process as a 'subreaper'. What that means is
  109. // even if the process uses the fork-exit technicque for
  110. // running a daemon (which normally orphans the process
  111. // and causes init(1) to adopt it, which is problematic
  112. // for TFB because we cannot then generally kill the
  113. // process since it has lost all context available to us)
  114. // the child process will have the parent id of THIS
  115. // process, allowing us to kill all the processes started
  116. // by the suite in this way generally.
  117. //
  118. // See: http://man7.org/linux/man-pages/man2/prctl.2.html
  119. prctl(PR_SET_CHILD_SUBREAPER,1);
  120. // This invokes whatever was passed as arguments to TFBReaper
  121. // on the system. This program is merely a pass-through to
  122. // a shell with the subreaper stuff enabled.
  123. int ret = system(result);
  124. // We need to wait forever; the suite will clean this
  125. // process up later.
  126. if (ret == 0) {
  127. for(;;) {
  128. // Pause to keep us from spiking CPU; whenever a signal
  129. // occurs (except SIGTERM etc which will kill this process)
  130. // just iterate and pause again.
  131. pause();
  132. }
  133. }
  134. // If the scripts failed, we should return that code.
  135. return ret;
  136. }