cleaned up debugcode
[forkaftergrep.git] / fag.c
1 /* forkaftergrep (C) 2017 Tobias Girstmair, GPLv3 */
2
3 #define _XOPEN_SOURCE 500
4 #define _BSD_SOURCE
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sysexits.h>
11 #include <sys/time.h>
12 #include <sys/wait.h>
13 #include <unistd.h>
14 #include "fag.h"
15
16 struct opt opts = {0, 0, NULL, NULL, STDOUT_FILENO};
17
18 int main (int argc, char** argv) {
19 int opt;
20 opterr = 0;
21
22 /* the `+' forces getopt to stop at the first non-option */
23 while ((opt = getopt (argc, argv, "+t:k::ehv")) != -1) {
24 switch (opt) {
25 case 't':
26 opts.timeout = atoi (optarg);
27 break;
28 case 'k':
29 opts.kill_sig = optarg ? atoi (optarg) : SIGTERM;
30 break;
31 case 'e':
32 opts.stream = STDERR_FILENO;
33 break;
34 case 'h':
35 fprintf (stderr, VERTEXT USAGE
36 "Options:\n"
37 "\t-t N\ttimeout after N seconds\n"
38 "\t-k [M]\tsend signal M to child after timeout (default: 15/SIGTERM)\n"
39 "\t-e\tgrep on stderr instead of stdout\n", argv[0]);
40 return EX_OK;
41 case 'v':
42 fprintf (stderr, VERTEXT);
43 return EX_OK;
44 default:
45 fprintf (stderr, "Unrecognized option: %c\n" USAGE, optopt, argv[0]);
46 return EX_USAGE;
47 }
48 }
49
50 /* the first non-option argument is the search string */
51 if (optind < argc) {
52 opts.pattern = argv[optind++];
53 } else {
54 fprintf (stderr, USAGE "(Missing PATTERN)\n", argv[0]);
55 return EX_USAGE;
56 }
57
58 /* the remaining are the program to be run */
59 if (optind < argc) {
60 opts.argv = &(argv[optind]);
61 } else {
62 fprintf (stderr, USAGE "(Missing PROGRAM)\n", argv[0]);
63 return EX_USAGE;
64 }
65
66 int retval = fork_after_grep (opts);
67
68 return retval;
69 }
70
71 int fork_after_grep (struct opt opts) {
72 int pipefd[2];
73 pid_t cpid;
74 int status;
75
76 char buf[BUF_SIZE];
77 int nbytes;
78
79 struct timeval begin, now, diff;
80
81 if (pipe(pipefd) == -1) {
82 fprintf (stderr, "pipe error\n");
83 return EX_OSERR;
84 }
85
86 if ((cpid = fork()) == -1) {
87 fprintf (stderr, "fork error: %s", strerror (errno));
88 close (pipefd[0]);
89 close (pipefd[1]);
90 return EX_OSERR;
91 }
92
93 if (cpid == 0) {
94 close (pipefd[0]);
95 dup2 (pipefd[1], opts.stream);
96 close (pipefd[1]);
97 close (opts.stream==STDOUT_FILENO?STDERR_FILENO:STDOUT_FILENO);
98
99 if (setsid () == -1) {
100 fprintf (stderr, "setsid error: %s", strerror (errno));
101 _exit (EX_OSERR);
102 }
103
104 execvp (opts.argv[0], opts.argv);
105 fprintf (stderr, "exec error: %s", strerror (errno));
106 _exit (EX_UNAVAILABLE);
107 } else {
108 close (pipefd[1]);
109 fcntl (pipefd[0], F_SETFL, fcntl (pipefd[0], F_GETFL, 0) | O_NONBLOCK);
110
111 gettimeofday (&begin, NULL);
112
113 for (;;) {
114 usleep (20000);
115 nbytes = read (pipefd[0], buf, BUF_SIZE);
116 if (nbytes == -1) {
117 switch (errno) {
118 case EAGAIN:
119 continue;
120 default:
121 fprintf (stderr, "read error: %s", strerror (errno));
122 close (pipefd[0]);
123 close (pipefd[1]);
124 return EX_IOERR;
125 }
126 } else if (nbytes == 0) {
127 fprintf (stderr, "Child program exited prematurely.\n");
128 close (pipefd[0]);
129 close (pipefd[1]);
130 if (waitpid (cpid, &status, WNOHANG) > 0 && WIFEXITED (status)) {
131 return WEXITSTATUS (status);
132 }
133 return EX_UNAVAILABLE;
134 }
135 if (strstr (buf, opts.pattern) != NULL) {
136 printf ("%d\n", cpid);
137 /* create a new child to keep pipe alive (will exit with exec'd program) */
138 if (!fork ()) {
139 while (kill(cpid, 0) != -1 && errno != ESRCH ) sleep (1);
140 close (pipefd[0]);
141 close (pipefd[1]);
142 _exit(0);
143 }
144 close (pipefd[0]);
145 close (pipefd[1]);
146 return EX_OK;
147 }
148
149 if (opts.timeout > 0) {
150 gettimeofday (&now, NULL);
151 timersub (&now, &begin, &diff);
152 if (diff.tv_sec >= opts.timeout) {
153 fprintf (stderr, "Timeout reached. \n");
154 if (opts.kill_sig > 0) kill (cpid, opts.kill_sig);
155 close (pipefd[0]);
156 close (pipefd[1]);
157 return EX_UNAVAILABLE;
158 }
159 }
160 }
161 }
162 }
Imprint / Impressum