]>
git.gir.st - forkaftergrep.git/blob - fag.c
1 /* forkaftergrep (C) 2017 Tobias Girstmair, GPLv3 */
2 //TODO: if grep exits with an error, fag thinks a match was found
3 //TODO: allow redirect of both streams to files
5 #define _XOPEN_SOURCE 500
6 #define _DEFAULT_SOURCE
21 void term_child(int s
) {
22 (void)s
; /* squash -Werror=unused-parameter */
23 if (cpid
!= 0) kill (cpid
, SIGTERM
);
27 int main (int argc
, char** argv
) {
28 struct opt opts
= {0, 0, 0, NULL
, NULL
, STDOUT_FILENO
, "-q"};
29 /* `-q': don't print anything; exit with 0 on match; with 1 on error. used to interface with `grep' */
33 /* generate grep options string */
34 char* p
= opts
.grepopt
+2; /* move cursor behind `q' */
36 signal (SIGPIPE
, SIG_IGN
); /* ignore broken pipe between fag and grep */
37 struct sigaction sa
= {0}; /* terminate child if fag gets interrupted/terminated */
38 sa
.sa_handler
= &term_child
; /* NOTE: will also be inherited by keep-pipe-alive-child */
39 sigaction (SIGINT
, &sa
, NULL
);
40 sigaction (SIGTERM
, &sa
, NULL
);
43 /* the `+' forces getopt to stop at the first non-option */
44 while ((opt
= getopt (argc
, argv
, "+t:k::er:VhvEFGPiwxyU")) != -1) {
47 opts
.timeout
= atoi (optarg
);
50 opts
.kill_sig
= optarg
? atoi (optarg
) : SIGTERM
;
53 opts
.stream
= STDERR_FILENO
;
59 fprintf (stderr
, VERTEXT USAGE
61 "\t-t N\ttimeout after N seconds\n"
62 "\t-k[M]\tsend signal M to child after timeout (default: 15/SIGTERM)\n"
63 "\t-e\tgrep on stderr instead of stdout\n"
64 "\t-V\tbe verbose; print PROGRAM's stdout/stderr to stderr\n"
65 "\t-[EFP]\tgrep: matcher selection\n"
66 "\t-[iwxU]\tgrep: matching control\n", argv
[0]);
69 fprintf (stderr
, VERTEXT
);
71 /* `grep' options (Note: missing `-e:', `-f:') */
72 case 'E': case 'F': case 'G': case 'P':
73 case 'i': case 'y': case 'w': case 'x':
74 case 'U': *(p
++)=opt
; break;
77 fprintf (stderr
, "Unrecognized option: %c\n" USAGE
, optopt
, argv
[0]);
81 *p
= '\0'; /* terminate grep_options string */
83 /* the first non-option argument is the search string */
85 opts
.pattern
= argv
[optind
++];
87 fprintf (stderr
, USAGE
"(Missing PATTERN)\n", argv
[0]);
91 /* the remaining are the program to be run */
93 opts
.argv
= &(argv
[optind
]);
95 fprintf (stderr
, USAGE
"(Missing PROGRAM)\n", argv
[0]);
99 int retval
= fork_after_grep (opts
);
104 int fork_after_grep (struct opt opts
) {
112 struct timeval begin
, now
, diff
;
114 if (pipe(pipefd
) == -1) {
115 fprintf (stderr
, "pipe error (userprog)\n");
119 if ((cpid
= fork()) == -1) {
120 fprintf (stderr
, "fork error (userprog): %s", strerror (errno
));
128 dup2 (pipefd
[1], opts
.stream
);
130 //dup2 (open("/dev/null", O_WRONLY), opts.stream==STDOUT_FILENO?STDERR_FILENO:STDOUT_FILENO);
131 close (opts
.stream
==STDOUT_FILENO
?STDERR_FILENO
:STDOUT_FILENO
);
133 if (setsid () == -1) {
134 fprintf (stderr
, "setsid error (userprog): %s", strerror (errno
));
138 execvp (opts
.argv
[0], opts
.argv
);
139 fprintf (stderr
, "exec error (userprog): %s", strerror (errno
));
140 _exit (EX_UNAVAILABLE
);
147 fcntl (pipefd
[0], F_SETFL
, fcntl (pipefd
[0], F_GETFL
, 0) | O_NONBLOCK
);
149 gettimeofday (&begin
, NULL
); /* for timeout */
151 if (pipe(grep_pipefd
) == -1) {
152 fprintf (stderr
, "pipe error (grep)\n");
157 if ((grep_cpid
= fork()) == -1) {
158 fprintf (stderr
, "fork error (grep): %s", strerror (errno
));
160 close (grep_pipefd
[0]);
161 close (grep_pipefd
[1]);
165 if (grep_cpid
== 0) {
166 close (grep_pipefd
[1]);
167 dup2 (grep_pipefd
[0], STDIN_FILENO
);
168 close (grep_pipefd
[0]);
170 close (STDERR_FILENO
);
171 close (STDOUT_FILENO
);
173 execlp ("grep", "grep", opts
.grepopt
, opts
.pattern
, NULL
);
174 fprintf (stderr
, "exec error (grep): %s", strerror (errno
));
177 close (grep_pipefd
[0]);
180 nbytes
= read (pipefd
[0], buf
, BUF_SIZE
);
186 fprintf (stderr
, "read error (userprog): %s", strerror (errno
));
188 close (grep_pipefd
[1]);
189 kill (grep_cpid
, SIGTERM
);
192 } else if (nbytes
== 0) {
193 fprintf (stderr
, "Child program exited prematurely (userprog).\n");
195 close (grep_pipefd
[1]);
196 kill (grep_cpid
, SIGTERM
);
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 */
204 write(STDERR_FILENO
, buf
, nbytes
);
207 write(grep_pipefd
[1], buf
, nbytes
); /* can cause SIGPIPE if grep exited, therefore signal will be ignored */
210 // TODO: exits with `0' even if `grep' exits with code > 0 !
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 int devnull
= open("/dev/null", O_WRONLY
);
221 dup2 (devnull
, pipefd
[0]);
223 while (kill(cpid
, 0) != -1 && errno
!= ESRCH
) sleep (1);
230 /* grep exited due to an error */
231 fprintf (stderr
, "grep exited due to an error.\n");
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
);
245 close (grep_pipefd
[1]);
246 kill (grep_cpid
, SIGTERM
);
247 return EX_UNAVAILABLE
;