remove unnecessary close()s, found bug (exit 141)
[forkaftergrep.git] / fag.c
CommitLineData
4f83e12d 1/* forkaftergrep (C) 2017 Tobias Girstmair, GPLv3 */
05257d9b 2//TODO: if grep exits with an error, fag thinks a match was found
fdb3674a 3//TODO: sometimes fag exits with code 141 (128+SIGPIPE)
4f83e12d 4
5#define _XOPEN_SOURCE 500
34f76df0 6#define _DEFAULT_SOURCE
4f83e12d 7#include <errno.h>
8#include <fcntl.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sysexits.h>
13#include <sys/time.h>
14#include <sys/wait.h>
15#include <unistd.h>
16#include "fag.h"
17
4f83e12d 18int main (int argc, char** argv) {
fdb3674a 19 struct opt opts = {0, 0, 0, NULL, NULL, STDOUT_FILENO, "-q"};
4f83e12d 20 int opt;
21 opterr = 0;
22
fdb3674a 23 /* generate grep options string */
24 /* `-q': don't print anything; exit with 0 on match; with 1 on error */
25 char* p = opts.grepopt+2; /* move cursor behind `q' */
26
27
4f83e12d 28 /* the `+' forces getopt to stop at the first non-option */
1e23aa5b 29 while ((opt = getopt (argc, argv, "+t:k::eVhvEFGPiwxyU")) != -1) {
4f83e12d 30 switch (opt) {
31 case 't':
32 opts.timeout = atoi (optarg);
33 break;
34 case 'k':
35 opts.kill_sig = optarg ? atoi (optarg) : SIGTERM;
36 break;
37 case 'e':
38 opts.stream = STDERR_FILENO;
39 break;
ff6565ef 40 case 'V':
41 opts.verbose = 1;
42 break;
4f83e12d 43 case 'h':
44 fprintf (stderr, VERTEXT USAGE
45 "Options:\n"
46 "\t-t N\ttimeout after N seconds\n"
47 "\t-k [M]\tsend signal M to child after timeout (default: 15/SIGTERM)\n"
329d9605 48 "\t-e\tgrep on stderr instead of stdout\n"
05257d9b 49 "\t-V\tbe verbose; print PROGRAM's stdout/stderr to stderr\n"
50 "\t-[EFGPiwxyU]\t grep options\n", argv[0]);
4f83e12d 51 return EX_OK;
52 case 'v':
53 fprintf (stderr, VERTEXT);
54 return EX_OK;
1e23aa5b 55 /* `grep' options (Note: missing `-e:', `-f:') */
fdb3674a 56 case 'E': case 'F': case 'G': case 'P':
57 case 'i': case 'y': case 'w': case 'x':
58 case 'U': *(p++)=opt; break;
1e23aa5b 59
4f83e12d 60 default:
61 fprintf (stderr, "Unrecognized option: %c\n" USAGE, optopt, argv[0]);
62 return EX_USAGE;
63 }
64 }
fdb3674a 65 *p = '\0'; /* finish grep_options string */
4f83e12d 66
67 /* the first non-option argument is the search string */
68 if (optind < argc) {
69 opts.pattern = argv[optind++];
70 } else {
71 fprintf (stderr, USAGE "(Missing PATTERN)\n", argv[0]);
72 return EX_USAGE;
73 }
74
75 /* the remaining are the program to be run */
76 if (optind < argc) {
77 opts.argv = &(argv[optind]);
78 } else {
79 fprintf (stderr, USAGE "(Missing PROGRAM)\n", argv[0]);
80 return EX_USAGE;
81 }
82
fdb3674a 83 int retval = fork_after_grep (opts);
4f83e12d 84
85 return retval;
86}
87
fdb3674a 88int fork_after_grep (struct opt opts) {
4f83e12d 89 int pipefd[2];
90 pid_t cpid;
91 int status;
92
93 char buf[BUF_SIZE];
94 int nbytes;
95
96 struct timeval begin, now, diff;
97
98 if (pipe(pipefd) == -1) {
f27db2a6 99 fprintf (stderr, "pipe error (userprog)\n");
4f83e12d 100 return EX_OSERR;
101 }
102
103 if ((cpid = fork()) == -1) {
f27db2a6 104 fprintf (stderr, "fork error (userprog): %s", strerror (errno));
4f83e12d 105 close (pipefd[0]);
106 close (pipefd[1]);
107 return EX_OSERR;
108 }
109
110 if (cpid == 0) {
111 close (pipefd[0]);
112 dup2 (pipefd[1], opts.stream);
113 close (pipefd[1]);
114 close (opts.stream==STDOUT_FILENO?STDERR_FILENO:STDOUT_FILENO);
115
116 if (setsid () == -1) {
f27db2a6 117 fprintf (stderr, "setsid error (userprog): %s", strerror (errno));
4f83e12d 118 _exit (EX_OSERR);
119 }
120
121 execvp (opts.argv[0], opts.argv);
f27db2a6 122 fprintf (stderr, "exec error (userprog): %s", strerror (errno));
4f83e12d 123 _exit (EX_UNAVAILABLE);
124 } else {
52503080 125 pid_t grep_cpid;
1e23aa5b 126 int grep_pipefd[2];
f27db2a6 127 int grep_status;
52503080 128
4f83e12d 129 close (pipefd[1]);
130 fcntl (pipefd[0], F_SETFL, fcntl (pipefd[0], F_GETFL, 0) | O_NONBLOCK);
131
d8642d91 132 gettimeofday (&begin, NULL); /* for timeout */
4f83e12d 133
1e23aa5b 134 if (pipe(grep_pipefd) == -1) {
f27db2a6 135 fprintf (stderr, "pipe error (grep)\n");
d8642d91 136 close (pipefd[0]);
52503080 137 return EX_OSERR;
138 }
f27db2a6 139
52503080 140 if ((grep_cpid = fork()) == -1) {
f27db2a6 141 fprintf (stderr, "fork error (grep): %s", strerror (errno));
52503080 142 close (pipefd[0]);
1e23aa5b 143 close (grep_pipefd[0]);
144 close (grep_pipefd[1]);
52503080 145 return EX_OSERR;
146 }
f27db2a6 147
52503080 148 if (grep_cpid == 0) {
1e23aa5b 149 close (grep_pipefd[1]);
150 dup2 (grep_pipefd[0], STDIN_FILENO);
151 close (grep_pipefd[0]);
f27db2a6 152
153 close (STDERR_FILENO);
154 close (STDOUT_FILENO);
155
fdb3674a 156 execlp ("grep", "grep", opts.grepopt, opts.pattern, NULL);
f27db2a6 157 fprintf (stderr, "exec error (grep): %s", strerror (errno));
05257d9b 158 _exit (EX_SOFTWARE);
52503080 159 } else {
1e23aa5b 160 close (grep_pipefd[0]);
52503080 161 for (;;) {
162 usleep (20000);
52503080 163 nbytes = read (pipefd[0], buf, BUF_SIZE);
164 if (nbytes == -1) {
165 switch (errno) {
166 case EAGAIN:
f27db2a6 167 break;
52503080 168 default:
f27db2a6 169 fprintf (stderr, "read error (userprog): %s", strerror (errno));
52503080 170 close (pipefd[0]);
1e23aa5b 171 close (grep_pipefd[1]);
d8642d91 172 //TODO: kill grep?
52503080 173 return EX_IOERR;
174 }
175 } else if (nbytes == 0) {
f27db2a6 176 fprintf (stderr, "Child program exited prematurely (userprog).\n");
4f83e12d 177 close (pipefd[0]);
1e23aa5b 178 close (grep_pipefd[1]);
d8642d91 179 //TODO: kill grep?
52503080 180 if (waitpid (cpid, &status, WNOHANG) > 0 && WIFEXITED (status)) {
181 return WEXITSTATUS (status);
182 }
183 return EX_UNAVAILABLE;
f27db2a6 184 } else {
185 /* have new userprog-data, send it to grep */
186 if (opts.verbose) {
1e23aa5b 187 write(STDERR_FILENO, buf, nbytes);
f27db2a6 188 }
189
1e23aa5b 190 write(grep_pipefd[1], buf, nbytes);
4f83e12d 191 }
f27db2a6 192
05257d9b 193 // TODO: exits with `0' even if `grep' exits with code > 0 !
f27db2a6 194 if (waitpid (grep_cpid, &grep_status, WNOHANG) > 0 && WIFEXITED (grep_status)) {
1e23aa5b 195 close (grep_pipefd[1]);
d8642d91 196
f27db2a6 197 if (WEXITSTATUS(grep_status) == 0) {
198 /* grep exited with match found */
199 printf ("%d\n", cpid);
d8642d91 200
f27db2a6 201 /* create a new child to keep pipe alive (will exit with exec'd program) */
202 if (!fork ()) {
203 while (kill(cpid, 0) != -1 && errno != ESRCH ) sleep (1);
204 close (pipefd[0]);
f27db2a6 205 _exit(0);
206 }
207 close (pipefd[0]);
f27db2a6 208 return EX_OK;
209 } else {
210 /* grep exited due to an error */
fdb3674a 211 fprintf (stderr, "grep exited due to an error.\n");
52503080 212 close (pipefd[0]);
1e23aa5b 213 close (grep_pipefd[1]);
f27db2a6 214 return EX_IOERR;
52503080 215 }
4f83e12d 216 }
4f83e12d 217
52503080 218 if (opts.timeout > 0) {
219 gettimeofday (&now, NULL);
220 timersub (&now, &begin, &diff);
221 if (diff.tv_sec >= opts.timeout) {
222 fprintf (stderr, "Timeout reached. \n");
223 if (opts.kill_sig > 0) kill (cpid, opts.kill_sig);
224 close (pipefd[0]);
1e23aa5b 225 close (grep_pipefd[1]);
d8642d91 226 //TODO: kill grep?
52503080 227 return EX_UNAVAILABLE;
228 }
4f83e12d 229 }
230 }
231 }
232 }
233}
Imprint / Impressum