]>
git.gir.st - forkaftergrep.git/blob - fag.c
1 /* forkaftergrep (C) 2017 Tobias Girstmair, GPLv3 */
2 //TODO: EX_UNAVAILABLE is wrongly used three times
4 #define _XOPEN_SOURCE 500
5 #define _DEFAULT_SOURCE
17 int main (int argc
, char** argv
) {
18 struct opt opts
= {0, 0, 0, NULL
, NULL
, STDOUT_FILENO
};
19 struct grepopt optg
= {basic_regexp
, 0, 0, 0, 0};
23 /* the `+' forces getopt to stop at the first non-option */
24 while ((opt
= getopt (argc
, argv
, "+t:k::eVhvEFGPiwxyU")) != -1) {
27 opts
.timeout
= atoi (optarg
);
30 opts
.kill_sig
= optarg
? atoi (optarg
) : SIGTERM
;
33 opts
.stream
= STDERR_FILENO
;
39 fprintf (stderr
, VERTEXT USAGE
41 "\t-t N\ttimeout after N seconds\n"
42 "\t-k [M]\tsend signal M to child after timeout (default: 15/SIGTERM)\n"
43 "\t-e\tgrep on stderr instead of stdout\n"
44 "\t-V\tbe verbose; print PROGRAM's stdout/stderr to stderr\n", argv
[0]);
47 fprintf (stderr
, VERTEXT
);
49 /* `grep' options (Note: missing `-e:', `-f:') */
50 case 'E': optg
.regex
= extended_regexp
; break;
51 case 'F': optg
.regex
= fixed_strings
; break;
52 case 'G': optg
.regex
= basic_regexp
; break; /* default */
53 case 'P': optg
.regex
= perl_regexp
; break;
54 case 'i': /* fall thru */
55 case 'y': optg
.ignore_case
= 1; break;
56 case 'w': optg
.word_regexp
= 1; break;
57 case 'x': optg
.line_regexp
= 1; break;
58 case 'U': optg
.binary
= 1; break;
61 fprintf (stderr
, "Unrecognized option: %c\n" USAGE
, optopt
, argv
[0]);
66 /* the first non-option argument is the search string */
68 opts
.pattern
= argv
[optind
++];
70 fprintf (stderr
, USAGE
"(Missing PATTERN)\n", argv
[0]);
74 /* the remaining are the program to be run */
76 opts
.argv
= &(argv
[optind
]);
78 fprintf (stderr
, USAGE
"(Missing PROGRAM)\n", argv
[0]);
82 int retval
= fork_after_grep (opts
, optg
);
87 int fork_after_grep (struct opt opts
, struct grepopt optg
) {
95 struct timeval begin
, now
, diff
;
97 if (pipe(pipefd
) == -1) {
98 fprintf (stderr
, "pipe error (userprog)\n");
102 if ((cpid
= fork()) == -1) {
103 fprintf (stderr
, "fork error (userprog): %s", strerror (errno
));
111 dup2 (pipefd
[1], opts
.stream
);
113 close (opts
.stream
==STDOUT_FILENO
?STDERR_FILENO
:STDOUT_FILENO
);
115 if (setsid () == -1) {
116 fprintf (stderr
, "setsid error (userprog): %s", strerror (errno
));
120 execvp (opts
.argv
[0], opts
.argv
);
121 fprintf (stderr
, "exec error (userprog): %s", strerror (errno
));
122 _exit (EX_UNAVAILABLE
);
129 fcntl (pipefd
[0], F_SETFL
, fcntl (pipefd
[0], F_GETFL
, 0) | O_NONBLOCK
);
131 gettimeofday (&begin
, NULL
); /* for timeout */
133 if (pipe(grep_pipefd
) == -1) {
134 fprintf (stderr
, "pipe error (grep)\n");
140 if ((grep_cpid
= fork()) == -1) {
141 fprintf (stderr
, "fork error (grep): %s", strerror (errno
));
144 close (grep_pipefd
[0]);
145 close (grep_pipefd
[1]);
149 if (grep_cpid
== 0) {
150 close (grep_pipefd
[1]);
151 dup2 (grep_pipefd
[0], STDIN_FILENO
);
152 close (grep_pipefd
[0]);
154 close (STDERR_FILENO
);
155 close (STDOUT_FILENO
);
157 /* generate argument list on the fly (TODO: ugly) */
158 /* `-q': don't print anything; exit with 0 on match; with 1 on error */
159 char grep_options
[16] = "-q";
160 char* p
= grep_options
+2;
161 if (optg
.regex
== extended_regexp
) *(p
++)='E';
162 if (optg
.regex
== fixed_strings
) *(p
++)='F';
163 if (optg
.regex
== basic_regexp
) *(p
++)='G';
164 if (optg
.regex
== perl_regexp
) *(p
++)='P';
165 if (optg
.ignore_case
) *(p
++)='i';
166 if (optg
.word_regexp
) *(p
++)='w';
167 if (optg
.line_regexp
) *(p
++)='x';
168 if (optg
.binary
) *(p
++)='U';
171 execlp ("grep", "grep", grep_options
, opts
.pattern
, NULL
);
172 fprintf (stderr
, "exec error (grep): %s", strerror (errno
));
173 _exit (EX_UNAVAILABLE
);
175 close (grep_pipefd
[0]);
178 nbytes
= read (pipefd
[0], buf
, BUF_SIZE
);
184 fprintf (stderr
, "read error (userprog): %s", strerror (errno
));
187 close (grep_pipefd
[1]);
191 } else if (nbytes
== 0) {
192 fprintf (stderr
, "Child program exited prematurely (userprog).\n");
195 close (grep_pipefd
[1]);
197 if (waitpid (cpid
, &status
, WNOHANG
) > 0 && WIFEXITED (status
)) {
198 return WEXITSTATUS (status
);
200 return EX_UNAVAILABLE
;
202 /* have new userprog-data, send it to grep */
205 write(STDERR_FILENO
, buf
, nbytes
);
208 write(grep_pipefd
[1], buf
, nbytes
);
211 if (waitpid (grep_cpid
, &grep_status
, WNOHANG
) > 0 && WIFEXITED (grep_status
)) {
212 close (grep_pipefd
[1]);
214 if (WEXITSTATUS(grep_status
) == 0) {
215 /* grep exited with match found */
216 printf ("%d\n", cpid
);
218 /* create a new child to keep pipe alive (will exit with exec'd program) */
220 while (kill(cpid
, 0) != -1 && errno
!= ESRCH
) sleep (1);
229 /* grep exited due to an error */
230 fprintf (stderr
, "grep exited due to an error.");
233 close (grep_pipefd
[1]);
238 if (opts
.timeout
> 0) {
239 gettimeofday (&now
, NULL
);
240 timersub (&now
, &begin
, &diff
);
241 if (diff
.tv_sec
>= opts
.timeout
) {
242 fprintf (stderr
, "Timeout reached. \n");
243 if (opts
.kill_sig
> 0) kill (cpid
, opts
.kill_sig
);
246 close (grep_pipefd
[1]);
248 return EX_UNAVAILABLE
;