e0218c36e2d119d42c60ad532783f886c4105a93
[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 printf ("timeout:\t%d\n" "kill_sig:\t%d\n" "pattern: \t%s\n" "stream: \t%d\n" "program: \t"
73 , opts.timeout, opts.kill_sig, opts.pattern, opts.stream);
74 for (char** p = opts.argv; *p;) printf ("%s ", *p++); putchar ('\n');
75
76 int pipefd[2];
77 pid_t cpid;
78 int status;
79
80 char buf[BUF_SIZE];
81 int nbytes;
82
83 struct timeval begin, now, diff;
84
85 if (pipe(pipefd) == -1) {
86 fprintf (stderr, "pipe error\n");
87 return EX_OSERR;
88 }
89
90 if ((cpid = fork()) == -1) {
91 fprintf (stderr, "fork error: %s", strerror (errno));
92 close (pipefd[0]);
93 close (pipefd[1]);
94 return EX_OSERR;
95 }
96
97 if (cpid == 0) {
98 close (pipefd[0]);
99 dup2 (pipefd[1], opts.stream);
100 close (pipefd[1]);
101 close (opts.stream==STDOUT_FILENO?STDERR_FILENO:STDOUT_FILENO);
102
103 if (setsid () == -1) {
104 fprintf (stderr, "setsid error: %s", strerror (errno));
105 _exit (EX_OSERR);
106 }
107
108 execvp (opts.argv[0], opts.argv);
109 fprintf (stderr, "exec error: %s", strerror (errno));
110 _exit (EX_UNAVAILABLE);
111 } else {
112 close (pipefd[1]);
113 fcntl (pipefd[0], F_SETFL, fcntl (pipefd[0], F_GETFL, 0) | O_NONBLOCK);
114
115 gettimeofday (&begin, NULL);
116
117 for (;;) {
118 usleep (20000);
119 nbytes = read (pipefd[0], buf, BUF_SIZE);
120 if (nbytes == -1) {
121 switch (errno) {
122 case EAGAIN:
123 continue;
124 default:
125 fprintf (stderr, "read error: %s", strerror (errno));
126 close (pipefd[0]);
127 close (pipefd[1]);
128 return EX_IOERR;
129 }
130 } else if (nbytes == 0) {
131 fprintf (stderr, "Child program exited prematurely.\n");
132 close (pipefd[0]);
133 close (pipefd[1]);
134 if (waitpid (cpid, &status, WNOHANG) > 0 && WIFEXITED (status)) {
135 return WEXITSTATUS (status);
136 }
137 return EX_UNAVAILABLE;
138 }
139 if (strstr (buf, opts.pattern) != NULL) {
140 printf ("%d\n", cpid);
141 /* create a new child to keep pipe alive (will exit with exec'd program) */
142 if (!fork ()) {
143 while (kill(cpid, 0) != -1 && errno != ESRCH ) sleep (1);
144 close (pipefd[0]);
145 close (pipefd[1]);
146 //close(0);close(1);close(2);
147 _exit(0);
148 }
149 close (pipefd[0]);
150 close (pipefd[1]);
151 //close(0);close(1);close(2);
152 return EX_OK;
153 }
154
155 if (opts.timeout > 0) {
156 gettimeofday (&now, NULL);
157 timersub (&now, &begin, &diff);
158 if (diff.tv_sec >= opts.timeout) {
159 fprintf (stderr, "Timeout reached. \n");
160 if (opts.kill_sig > 0) kill (cpid, opts.kill_sig);
161 close (pipefd[0]);
162 close (pipefd[1]);
163 return EX_UNAVAILABLE;
164 }
165 }
166 }
167 }
168 }
Imprint / Impressum