]>
git.gir.st - forkaftergrep.git/blob - fag.c
1 /* forkaftergrep (C) 2017 Tobias Girstmair, GPLv3 */
2 //TODO: allow redirect of both streams to files (have to simulate `tee(1)' in the pipe-passer and the dummy-proc; instead of closing nontracked fd redirect to logfile/devnull)
3 //TODO: grep is missing `-e' and `-f' options
5 #define _XOPEN_SOURCE 500
6 #define _DEFAULT_SOURCE
22 void term_child ( int s
) {
23 if ( cpid
!= 0 ) kill ( cpid
, s
);
27 int main ( int argc
, char ** argv
) {
28 struct opt opts
= { 0 , 0 , 0 , NULL
, NULL
, "/dev/null" , "/dev/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::rl:L:VhvEFGPiwxyUZJe:f:" )) != - 1 ) {
47 opts
. timeout
= atoi ( optarg
);
50 opts
. kill_sig
= optarg
? atoi ( optarg
) : SIGTERM
;
53 opts
. stream
= STDERR_FILENO
;
56 fprintf ( stderr
, "WARNING: `-l' not currently implemented \n " );
60 fprintf ( stderr
, "WARNING: `-L' not currently implemented \n " );
67 fprintf ( stderr
, VERTEXT USAGE
69 " \t -t N \t timeout after N seconds \n "
70 " \t -k[N] \t send signal N to child after timeout (default: 15/SIGTERM) \n "
71 " \t -r \t grep on stderr instead of stdout \n "
72 " \t -l FILE \t log PROGRAM's stdout to FILE \n "
73 " \t -L FILE \t log PROGRAM's stderr to FILE \n "
74 " \t -V \t be verbose; print monitored stream to stderr \n "
75 " \t -[EFGP] \t grep: matcher selection \n "
76 " \t -[iwxU] \t grep: matching control \n "
77 " \t -[ZJ] \t grep: decompression control \n " , argv
[ 0 ]);
80 fprintf ( stderr
, VERTEXT
);
83 fprintf ( stderr
, "`grep -e' and `-f' are not implemented yet! \n " );
86 case 'E' : case 'F' : case 'G' : case 'P' :
87 case 'i' : case 'y' : case 'w' : case 'x' :
88 case 'U' : case 'Z' : case 'J' :
93 fprintf ( stderr
, "Unrecognized option: %c \n " USAGE
, optopt
, argv
[ 0 ]);
97 * p
= '\0' ; /* terminate grep_options string */
99 /* the first non-option argument is the search string */
101 opts
. pattern
= argv
[ optind
++];
103 fprintf ( stderr
, USAGE
"(Missing PATTERN) \n " , argv
[ 0 ]);
107 /* the remaining are the program to be run */
109 opts
. argv
= &( argv
[ optind
]);
111 fprintf ( stderr
, USAGE
"(Missing PROGRAM) \n " , argv
[ 0 ]);
115 int retval
= fork_after_grep ( opts
);
120 int fork_after_grep ( struct opt opts
) {
122 int pipefd_cpid
[ 2 ]; /* IPC to extract PID from daemonized userprog */
129 struct timeval begin
, now
, diff
;
131 if ( pipe ( pipefd
) == - 1 ) {
132 fprintf ( stderr
, "pipe error (userprog) \n " );
136 if ( pipe ( pipefd_cpid
) == - 1 ) {
137 fprintf ( stderr
, "pipe error (cpid-ipc) \n " );
143 if (( tmp_cpid
= fork ()) == - 1 ) {
144 fprintf ( stderr
, "fork error (daemonizer): %s" , strerror ( errno
));
147 close ( pipefd_cpid
[ 0 ]);
148 close ( pipefd_cpid
[ 1 ]);
156 dup2 ( pipefd
[ 1 ], opts
. stream
);
158 //dup2 (open("/dev/null", O_WRONLY), opts.stream==STDOUT_FILENO?STDERR_FILENO:STDOUT_FILENO);
159 close ( opts
. stream
== STDOUT_FILENO
? STDERR_FILENO
: STDOUT_FILENO
);
161 if ( setsid () == - 1 ) {
162 fprintf ( stderr
, "setsid error (daemonizer): %s" , strerror ( errno
));
166 if (( cpid_userprog
= fork ()) == - 1 ) {
167 fprintf ( stderr
, "fork error (userprog): %s" , strerror ( errno
));
170 if ( cpid_userprog
== 0 ) {
171 close ( pipefd_cpid
[ 0 ]);
172 close ( pipefd_cpid
[ 1 ]);
174 execvp ( opts
. argv
[ 0 ], opts
. argv
);
175 fprintf ( stderr
, "exec error (userprog): %s" , strerror ( errno
));
177 /* only way to get final child's pid to main is through IPC */
178 write ( pipefd_cpid
[ 1 ], & cpid_userprog
, sizeof ( pid_t
));
179 close ( pipefd_cpid
[ 0 ]);
180 close ( pipefd_cpid
[ 1 ]);
182 _exit ( EX_UNAVAILABLE
);
188 /* read userprog's PID from IPC: */
189 read ( pipefd_cpid
[ 0 ], & cpid
, sizeof ( pid_t
));
190 close ( pipefd_cpid
[ 0 ]);
191 close ( pipefd_cpid
[ 1 ]);
194 fcntl ( pipefd
[ 0 ], F_SETFL
, fcntl ( pipefd
[ 0 ], F_GETFL
, 0 ) | O_NONBLOCK
);
196 gettimeofday (& begin
, NULL
); /* for timeout */
198 if ( pipe ( grep_pipefd
) == - 1 ) {
199 fprintf ( stderr
, "pipe error (grep) \n " );
204 if (( grep_cpid
= fork ()) == - 1 ) {
205 fprintf ( stderr
, "fork error (grep): %s" , strerror ( errno
));
207 close ( grep_pipefd
[ 0 ]);
208 close ( grep_pipefd
[ 1 ]);
212 if ( grep_cpid
== 0 ) {
213 close ( grep_pipefd
[ 1 ]);
214 dup2 ( grep_pipefd
[ 0 ], STDIN_FILENO
);
215 close ( grep_pipefd
[ 0 ]);
217 close ( STDOUT_FILENO
);
219 execlp ( "grep" , "grep" , opts
. grepopt
, "--" , opts
. pattern
, NULL
);
220 fprintf ( stderr
, "exec error (grep): %s" , strerror ( errno
));
223 close ( grep_pipefd
[ 0 ]);
226 nbytes
= read ( pipefd
[ 0 ], buf
, BUF_SIZE
);
232 fprintf ( stderr
, "read error (userprog): %s" , strerror ( errno
));
234 close ( grep_pipefd
[ 1 ]);
235 kill ( grep_cpid
, SIGTERM
);
238 } else if ( nbytes
== 0 ) {
239 fprintf ( stderr
, "Child program exited prematurely (userprog). \n " );
241 close ( grep_pipefd
[ 1 ]);
242 kill ( grep_cpid
, SIGTERM
);
243 if ( waitpid ( cpid
, & status
, WNOHANG
) > 0 && WIFEXITED ( status
)) {
244 return WEXITSTATUS ( status
);
246 return EX_UNAVAILABLE
;
248 /* have new userprog-data, send it to grep */
250 write ( STDERR_FILENO
, buf
, nbytes
);
253 write ( grep_pipefd
[ 1 ], buf
, nbytes
); /* can cause SIGPIPE if grep exited, therefore signal will be ignored */
256 if ( waitpid ( grep_cpid
, & grep_status
, WNOHANG
) > 0 && WIFEXITED ( grep_status
)) {
257 close ( grep_pipefd
[ 1 ]);
259 if ( WEXITSTATUS ( grep_status
) == 0 ) {
260 /* grep exited with match found */
261 printf ( "%d \n " , cpid
);
264 /* create a new child to keep pipe alive, empty it and write log files (will exit with exec'd program) */
265 if (! fork ()){ setsid (); if (! fork ()) {
266 close ( 0 ); close ( 1 ); close ( 2 ); umask ( 0 ); chdir ( "/" );
267 int devnull
= open ( "/dev/null" , O_WRONLY
);
268 dup2 ( devnull
, pipefd
[ 0 ]);
270 while ( kill ( cpid
, 0 ) != - 1 && errno
!= ESRCH
) sleep ( 1 );
277 /* grep exited due to an error */
278 fprintf ( stderr
, "grep exited due to an error. \n " );
280 close ( grep_pipefd
[ 1 ]);
285 if ( opts
. timeout
> 0 ) {
286 gettimeofday (& now
, NULL
);
287 timersub (& now
, & begin
, & diff
);
288 if ( diff
. tv_sec
>= opts
. timeout
) {
289 fprintf ( stderr
, "Timeout reached. \n " );
290 if ( opts
. kill_sig
> 0 ) kill ( cpid
, opts
. kill_sig
);
292 close ( grep_pipefd
[ 1 ]);
293 kill ( grep_cpid
, SIGTERM
);
294 return EX_UNAVAILABLE
;