PageRenderTime 27ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/multitail-5.2.8/exec.c

#
C | 413 lines | 270 code | 76 blank | 67 comment | 113 complexity | 485c361ca3beddad2dd2548a09d22702 MD5 | raw file
Possible License(s): AGPL-1.0
  1. #define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */
  2. #include <sys/types.h>
  3. #include <regex.h>
  4. #include <sys/stat.h>
  5. #include <unistd.h>
  6. #include <sys/wait.h>
  7. #include <errno.h>
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <signal.h>
  12. #include <sys/socket.h>
  13. #include <netinet/in.h>
  14. #include "mt.h"
  15. #include "error.h"
  16. #include "utils.h"
  17. #include "mem.h"
  18. #include "term.h"
  19. #include "my_pty.h"
  20. #include "globals.h"
  21. #include "ui.h"
  22. int start_tail(char *filename, char retry_open, char follow_filename, int initial_tail, int *pipefd)
  23. {
  24. pid_t pid;
  25. /* start child process */
  26. if ((pid = fork()) == 0)
  27. {
  28. char *pars[16], *posix_version = NULL;
  29. int npars = 0;
  30. char nlines_buffer[32];
  31. setpgid(0,0);
  32. setup_for_childproc(pipefd[1], 0, "dumb");
  33. /* create command for take last n lines & follow and start tail */
  34. /* the command to start */
  35. pars[npars++] = tail;
  36. /* Linux' tail has the --retry option, but not all
  37. * other UNIX'es have this, so I implemented it
  38. * myself
  39. */
  40. if (retry_open)
  41. {
  42. int rc;
  43. struct stat64 buf;
  44. for(;;)
  45. {
  46. rc = stat64(filename, &buf);
  47. if (rc == -1)
  48. {
  49. if (errno != ENOENT)
  50. {
  51. fprintf(stderr, "Error while looking for file %s: %d\n", filename, errno);
  52. exit(EXIT_FAILURE);
  53. }
  54. }
  55. else if (rc == 0)
  56. break;
  57. usleep(WAIT_FOR_FILE_DELAY * 1000);
  58. }
  59. #if defined(linux) || defined(__CYGWIN__) || defined(__GNU__)
  60. pars[npars++] = "--retry";
  61. #endif
  62. }
  63. /* get the posix compliance level */
  64. posix_version = getenv("_POSIX2_VERSION");
  65. /* follow filename is only supported on *BSD and Linux */
  66. #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(linux) || defined(__CYGWIN__) || defined(__APPLE__) || defined(__GNU__)
  67. if (follow_filename)
  68. {
  69. #if defined(linux) || defined(__CYGWIN__) || defined(__GNU__)
  70. pars[npars++] = "--follow=name";
  71. #elif defined(__OpenBSD__)
  72. pars[npars++] = "-f";
  73. #else
  74. pars[npars++] = "-F";
  75. #endif
  76. /* number of lines to tail initially */
  77. pars[npars++] = "-n";
  78. snprintf(nlines_buffer, sizeof(nlines_buffer), "%d", initial_tail);
  79. pars[npars++] = nlines_buffer;
  80. }
  81. else
  82. #endif
  83. {
  84. #if !defined(linux) && !defined(__CYGWIN__) && !defined(__GNU__)
  85. if (follow_filename && gnu_tail)
  86. pars[npars++] = "--follow=name";
  87. #endif
  88. /* check the posix compliance level */
  89. if ((posix_version && atoi(posix_version) >= 200112) || posix_tail == MY_TRUE)
  90. {
  91. pars[npars++] = "-f";
  92. pars[npars++] = "-n";
  93. snprintf(nlines_buffer, sizeof(nlines_buffer), "%d", initial_tail);
  94. pars[npars++] = nlines_buffer;
  95. }
  96. else
  97. {
  98. /* number of lines to tail initially and 'follow file' ('f') */
  99. snprintf(nlines_buffer, sizeof(nlines_buffer), "-%dlf", initial_tail);
  100. pars[npars++] = nlines_buffer;
  101. }
  102. }
  103. /* add the filename to monitor */
  104. pars[npars++] = filename;
  105. pars[npars] = NULL;
  106. /* run tail! */
  107. if (-1 == execvp(pars[0], pars)) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error while starting process %s.\n", pars[0]);
  108. /* if execlp returns, an error occured */
  109. error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "An error occured while starting process %s!\n", pars[0]);
  110. }
  111. return pid;
  112. }
  113. void start_proc_signal_handler(int sig)
  114. {
  115. if (sig != SIGTERM) error_popup("Signal handler(2)", -1, "Unexpected signal %d.\n", sig);
  116. stop_process(tail_proc);
  117. exit(1);
  118. }
  119. int start_proc(proginfo *cur, int initial_tail)
  120. {
  121. cur -> n_runs++;
  122. if (cur -> wt == WT_COMMAND)
  123. {
  124. int fd_master, fd_slave;
  125. /* allocate pseudo-tty & fork*/
  126. cur -> pid = get_pty_and_fork(&fd_master, &fd_slave);
  127. if (-1 == cur -> pid) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "An error occured while invoking get_pty_and_fork.\n");
  128. /* child? */
  129. if (cur -> pid == 0)
  130. {
  131. setpgid(0,0);
  132. /* reset signal handler for SIGTERM */
  133. signal(SIGTERM, SIG_DFL);
  134. /* sleep if requested and only when 2nd or 3d (etc.) execution time */
  135. if (cur -> restart.restart && cur -> restart.first == 0)
  136. sleep(cur -> restart.restart);
  137. /* connect slave-fd to stdin/out/err */
  138. setup_for_childproc(fd_slave, 1, term_t_to_string(cur -> cdef.term_emul));
  139. /* start process */
  140. if (-1 == execlp(shell, shell, "-c", cur -> filename, (void *)NULL)) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error while starting \"%s -c '%s'\".\n", shell, cur -> filename);
  141. /* if execlp returns, an error occured */
  142. error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error while starting process %s!\n", shell);
  143. }
  144. #if defined(sun) || defined(__sun) || defined(AIX) || defined(_HPUX_SOURCE) || defined(OSF1) || defined(scoos)
  145. /* these platforms only have the slave-fd available in the childprocess
  146. * * so don't try to close it as the parent process doesn't have it
  147. *
  148. */
  149. #else
  150. if (myclose(fd_slave) == -1) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "An error occured while closing the slave fd (pseudo tty, fd %d)\n", fd_slave);
  151. #endif
  152. /* remember master-fd (we'll read from that one) */
  153. cur -> fd = fd_master;
  154. cur -> wfd = fd_master;
  155. /* next time, sleep */
  156. cur -> restart.first = 0;
  157. }
  158. else if (cur -> wt == WT_FILE)
  159. {
  160. int pipefd[2];
  161. /* create a pipe, will be to child-process */
  162. if (-1 == pipe(pipefd)) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error creating pipe.\n");
  163. if (cur -> check_interval)
  164. {
  165. /* start the process that will check every 'interval' seconds
  166. * if a matching file with a more recent mod-time is available
  167. */
  168. cur -> pid = fork();
  169. if (cur -> pid == 0)
  170. {
  171. char *cur_file = NULL, *new_file;
  172. setpgid(0,0);
  173. signal(SIGTERM, start_proc_signal_handler);
  174. for(;;)
  175. {
  176. /* find (new) file */
  177. new_file = find_most_recent_file(cur -> filename, cur_file);
  178. /* if find_most_recent_file returned NOT null, a file was found
  179. * which is more recent
  180. */
  181. if (new_file != NULL)
  182. {
  183. /* if there was a previous file, see if it is different
  184. * from the new filename. this should always be the case!
  185. */
  186. if (cur_file && strcmp(new_file, cur_file) != 0)
  187. {
  188. stop_process(tail_proc);
  189. }
  190. /* remember new filename */
  191. myfree(cur_file);
  192. cur_file = new_file;
  193. /* and start a proc process */
  194. tail_proc = start_tail(cur_file, cur -> retry_open, cur -> follow_filename, initial_tail, pipefd);
  195. if (tail_proc == -1)
  196. {
  197. break;
  198. }
  199. }
  200. else
  201. {
  202. /* LOG("no file found for pattern %s\n", cur -> filename); */
  203. }
  204. sleep(cur -> check_interval);
  205. }
  206. /* LOG("stopped checking for file pattern %s\n", cur -> filename); */
  207. exit(1);
  208. }
  209. }
  210. else
  211. {
  212. cur -> pid = start_tail(cur -> filename, cur -> retry_open, cur -> follow_filename, initial_tail, pipefd);
  213. }
  214. cur -> fd = pipefd[0];
  215. cur -> wfd = pipefd[1];
  216. }
  217. if (cur -> pid > -1)
  218. return 0;
  219. return -1;
  220. }
  221. int execute_program(char *execute, char bg)
  222. {
  223. int status;
  224. pid_t child;
  225. if (bg)
  226. {
  227. /* to prevent meltdowns, only a limited number of
  228. * processes can be executed
  229. */
  230. if (n_children >= MAX_N_SPAWNED_PROCESSES)
  231. return 0;
  232. }
  233. else
  234. endwin();
  235. child = fork();
  236. if (child == 0)
  237. {
  238. setpgid(0,0);
  239. if (bg)
  240. setup_for_childproc(open_null(), 1, "dumb");
  241. /* start process */
  242. if (-1 == execlp(shell, shell, "-c", execute, (void *)NULL)) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error while starting \"%s -c '%s'\".\n", execute);
  243. /* if execlp returns, an error occured */
  244. error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error while starting process!\n");
  245. }
  246. else if (child == -1)
  247. {
  248. error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Failed to fork child process.\n");
  249. }
  250. if (bg)
  251. {
  252. /* remember this childprocess: we'll see if it has
  253. * died in the main-loop
  254. */
  255. children_list[n_children++] = child;
  256. }
  257. else
  258. {
  259. /* wait for the childprocess to exit */
  260. if (waitpid(child, &status, 0) == -1)
  261. error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error while waiting for process to exit.\n");
  262. /* restore (n)curses */
  263. mydoupdate();
  264. }
  265. return 0;
  266. }
  267. void init_children_reaper(void)
  268. {
  269. /* init list of pids to watch for exit */
  270. memset(children_list, 0x00, sizeof(children_list));
  271. }
  272. pid_t exec_with_pipe(char *command, int pipe_to_proc[], int pipe_from_proc[])
  273. {
  274. pid_t pid = -1;
  275. if (pipe(pipe_to_proc) == -1)
  276. error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error creating pipe.\n");
  277. if (pipe(pipe_from_proc) == -1)
  278. error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error creating pipe.\n");
  279. if ((pid = fork()) == -1)
  280. error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "fork() failed.\n");
  281. if (pid == 0)
  282. {
  283. myclose(0);
  284. if (mydup(pipe_to_proc[0]) == -1) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "dup() failed.\n");
  285. myclose(pipe_to_proc[1]); /* will not write to itself, only parent writes to it */
  286. myclose(1);
  287. myclose(2);
  288. if (mydup(pipe_from_proc[1]) == -1) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "dup() failed.\n");
  289. if (mydup(pipe_from_proc[1]) == -1) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "dup() failed.\n");
  290. myclose(pipe_from_proc[0]);
  291. /* start process */
  292. /* if (-1 == execlp(shell, shell, "-c", command, (void *)NULL)) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "execlp of %s failed\n", command); */
  293. if (-1 == execlp(command, command, (void *)NULL)) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error while starting '%s'.\n", command);
  294. /* if execlp returns, an error occured */
  295. error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error while starting process '%s'.\n", command);
  296. }
  297. return pid;
  298. }
  299. pid_t exec_with_pty(char *command, int *fd)
  300. {
  301. int fd_master = -1, fd_slave = -1;
  302. pid_t pid = get_pty_and_fork(&fd_master, &fd_slave);
  303. if (-1 == pid) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "An error occured while invoking get_pty_and_fork.\n");
  304. if (pid == 0)
  305. {
  306. setpgid(0,0);
  307. myclose(fd_master);
  308. /* connect slave-fd to stdin/out/err */
  309. setup_for_childproc(fd_slave, 1, "dumb");
  310. /* start process */
  311. if (-1 == execlp(command, command, (void *)NULL)) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error while starting '%s'.\n", command);
  312. }
  313. *fd = fd_master;
  314. #if defined(sun) || defined(__sun) || defined(AIX) || defined(_HPUX_SOURCE) || defined(OSF1) || defined(scoos)
  315. /* see start_proc */
  316. #else
  317. if (myclose(fd_slave) == -1) error_exit(__FILE__, __PRETTY_FUNCTION__, __LINE__, "Error closing slave-fd (pseudo tty, fd %d)\n", fd_slave);
  318. #endif
  319. return pid;
  320. }
  321. void exec_script(script *pscript)
  322. {
  323. if (pscript -> pid == 0)
  324. {
  325. int to[2], from[2];
  326. pscript -> pid = exec_with_pipe(pscript -> script, to, from);
  327. pscript -> fd_r = from[0];
  328. pscript -> fd_w = to[1];
  329. /*
  330. int fd;
  331. pscript -> pid = exec_with_pty(pscript -> script, &fd);
  332. pscript -> fd_r =
  333. pscript -> fd_w = fd;
  334. */
  335. }
  336. }