ctorrent stat collector
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.

218 lines
5.3KB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <sys/wait.h>
  8. #include <errno.h>
  9. #include <termios.h>
  10. #include <pty.h>
  11. #include <fcntl.h>
  12. #include "list.h"
  13. #include "logging.h"
  14. #include "process.h"
  15. #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
  16. static LIST_HEAD(child_proc_list);
  17. struct child_process * childproc_alloc(char *const argv[], const char *pwd)
  18. {
  19. /* count args */
  20. int cnt = 0;
  21. while (argv[cnt++] != NULL);
  22. /* alloc struct child_process and array of char **argv */
  23. int size = sizeof(struct child_process) + sizeof(char *) * cnt;
  24. struct child_process *child = malloc(size);
  25. if (child == NULL) {
  26. log_print(LOG_ERROR, "create_child_process(): malloc()");
  27. return NULL;
  28. }
  29. memset(child, 0, sizeof(struct child_process));
  30. /* argv points to first byte after struct child_process */
  31. child->argv = (char **)(child +1);
  32. int i;
  33. for (i = 0; i < ARRAY_SIZE(child->fd); i++)
  34. child->fd[i] = -1;
  35. /* copy argv */
  36. for (i = 0; i < cnt; i++)
  37. child->argv[i] = (argv[i] != NULL) ? strdup(argv[i]) : NULL;
  38. /* copy pwd */
  39. if (pwd != NULL)
  40. child->pwd = strdup(pwd);
  41. return child;
  42. }
  43. int childproc_free(struct child_process *child)
  44. {
  45. /* child already running, return error */
  46. if (child->pid != 0) {
  47. log_print(LOG_ERROR, "childproc_free(): process [pid:%d] already running", child->pid);
  48. return -1;
  49. }
  50. int i;
  51. for (i = 0; child->argv[i] != NULL; i++)
  52. free(child->argv[i]);
  53. if (child->pwd != NULL)
  54. free(child->pwd);
  55. free(child);
  56. return 0;
  57. }
  58. pid_t childproc_fork(struct child_process *child, void (*exit_cb)(struct child_process *child, int exit_code, void *privdata), void *privdata)
  59. {
  60. struct stat stat_buf;
  61. if (stat(child->argv[0], &stat_buf) != 0) {
  62. log_print(LOG_ERROR, "childproc_fork(): stat()");
  63. return -1;
  64. /* not a regular file, or not executable */
  65. } else if (!S_ISREG(stat_buf.st_mode) || !(stat_buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
  66. log_print(LOG_ERROR, "childproc_fork(): stat()");
  67. return -1;
  68. }
  69. /* exit callback */
  70. child->exit_cb = exit_cb;
  71. child->privdata = privdata;
  72. /*
  73. * fd[0] - if -1 pipe() and dup2() read-side as child stdin, store write-side for parent
  74. * else dup2() this fd as child stdin
  75. * fd[1] - if -1 pipe() and dup2() write-side as child stdout, store read-side for parent
  76. * else dup2() this fd as child stdout
  77. * fd[2] - if -1 pipe() and dup2() write-side as child stderr, store read-side for parent
  78. * else dup2() this fd as child stderr
  79. */
  80. int child_pipes[3][2];
  81. if (child->fd[STDIN_FILENO] == -1) {
  82. if (pipe(child_pipes[STDIN_FILENO]) < 0) {
  83. log_print(LOG_ERROR, "childproc_fork(): pipe(STDIN_FILENO)");
  84. return -1;
  85. }
  86. } else {
  87. child_pipes[STDIN_FILENO][0] = child->fd[STDIN_FILENO];
  88. child_pipes[STDIN_FILENO][1] = -1;
  89. }
  90. if (child->fd[STDOUT_FILENO] == -1) {
  91. if (pipe(child_pipes[STDOUT_FILENO]) < 0) {
  92. log_print(LOG_ERROR, "childproc_fork(): pipe(STDOUT_FILENO)");
  93. return -1;
  94. }
  95. } else {
  96. child_pipes[STDOUT_FILENO][0] = -1;
  97. child_pipes[STDOUT_FILENO][1] = child->fd[STDOUT_FILENO];
  98. }
  99. if (child->fd[STDERR_FILENO] == -1) {
  100. if (pipe(child_pipes[STDERR_FILENO]) < 0) {
  101. log_print(LOG_ERROR, "childproc_fork(): pipe(STDERR_FILENO)");
  102. return -1;
  103. }
  104. } else {
  105. child_pipes[STDERR_FILENO][0] = -1;
  106. child_pipes[STDERR_FILENO][1] = child->fd[STDERR_FILENO];
  107. }
  108. /* add this to list now, so sigchld_handler can find it */
  109. list_add_tail(&child->list, &child_proc_list);
  110. child->pid = fork();
  111. if (child->pid == 0) { /* child */
  112. close(child_pipes[STDIN_FILENO][1]);
  113. close(child_pipes[STDOUT_FILENO][0]);
  114. close(child_pipes[STDERR_FILENO][0]);
  115. if (dup2(child_pipes[STDIN_FILENO][0], STDIN_FILENO) < 0) {
  116. perror("dup2(STDIN_FILENO)");
  117. exit(1);
  118. }
  119. if (dup2(child_pipes[STDOUT_FILENO][1], STDOUT_FILENO) < 0) {
  120. perror("dup2(STDOUT_FILENO)");
  121. exit(1);
  122. }
  123. if (dup2(child_pipes[STDERR_FILENO][1], STDERR_FILENO) < 0) {
  124. perror("dup2(STDERR_FILENO)");
  125. exit(1);
  126. }
  127. close(child_pipes[STDIN_FILENO][0]);
  128. close(child_pipes[STDOUT_FILENO][1]);
  129. close(child_pipes[STDERR_FILENO][1]);
  130. if (child->pwd != NULL && chdir(child->pwd) < 0) {
  131. perror("chdir()");
  132. exit(1);
  133. }
  134. execv(child->argv[0], child->argv);
  135. perror("execv()");
  136. exit(1);
  137. } else if (child->pid < 0) { /* fork error */
  138. log_print(LOG_ERROR, "childproc_fork(): fork()");
  139. return -1;
  140. } else { /* parent */
  141. if (child->fd[STDIN_FILENO] == -1) {
  142. close(child_pipes[STDIN_FILENO][0]);
  143. child->fd[STDIN_FILENO] = child_pipes[STDIN_FILENO][1];
  144. }
  145. if (child->fd[STDOUT_FILENO] == -1) {
  146. close(child_pipes[STDOUT_FILENO][1]);
  147. child->fd[STDOUT_FILENO] = child_pipes[STDOUT_FILENO][0];
  148. }
  149. if (child->fd[STDERR_FILENO] == -1) {
  150. close(child_pipes[STDERR_FILENO][1]);
  151. child->fd[STDERR_FILENO] = child_pipes[STDERR_FILENO][0];
  152. }
  153. return child->pid;
  154. }
  155. }
  156. void childproc_cleanup(void)
  157. {
  158. struct child_process *child, *tmp;
  159. list_for_each_entry_safe(child, tmp, &child_proc_list, list) {
  160. int status = 0;
  161. int ret = waitpid(child->pid, &status, WNOHANG);
  162. if (ret == -1) {
  163. log_print(LOG_WARN, "childproc_cleanup(): waitpid(%d)", child->pid);
  164. continue;
  165. } else if (ret != 0 && WIFEXITED(status)) {
  166. list_del(&child->list);
  167. if (child->exit_cb != NULL)
  168. child->exit_cb(child, WEXITSTATUS(status), child->privdata);
  169. child->pid = 0;
  170. childproc_free(child);
  171. }
  172. }
  173. }