replace deprecated _BSD_SOURCE with _DEFAULT_SOURCE
[forkaftergrep.git] / fag.c
1 /* forkaftergrep (C) 2017 Tobias Girstmair, GPLv3 */
2
3 #define _XOPEN_SOURCE 500
4 #define _DEFAULT_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, 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::eVhv")) != -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 'V':
35 opts.verbose = 1;
36 break;
37 case 'h':
38 fprintf (stderr, VERTEXT USAGE
39 "Options:\n"
40 "\t-t N\ttimeout after N seconds\n"
41 "\t-k [M]\tsend signal M to child after timeout (default: 15/SIGTERM)\n"
42 "\t-e\tgrep on stderr instead of stdout\n", argv[0]);
43 return EX_OK;
44 case 'v':
45 fprintf (stderr, VERTEXT);
46 return EX_OK;
47 default:
48 fprintf (stderr, "Unrecognized option: %c\n" USAGE, optopt, argv[0]);
49 return EX_USAGE;
50 }
51 }
52
53 /* the first non-option argument is the search string */
54 if (optind < argc) {
55 opts.pattern = argv[optind++];
56 } else {
57 fprintf (stderr, USAGE "(Missing PATTERN)\n", argv[0]);
58 return EX_USAGE;
59 }
60
61 /* the remaining are the program to be run */
62 if (optind < argc) {
63 opts.argv = &(argv[optind]);
64 } else {
65 fprintf (stderr, USAGE "(Missing PROGRAM)\n", argv[0]);
66 return EX_USAGE;
67 }
68
69 int retval = fork_after_grep (opts);
70
71 return retval;
72 }
73
74 int fork_after_grep (struct opt opts) {
75 int pipefd[2];
76 pid_t cpid;
77 int status;
78
79 char buf[BUF_SIZE];
80 int nbytes;
81
82 struct timeval begin, now, diff;
83
84 if (pipe(pipefd) == -1) {
85 fprintf (stderr, "pipe error\n");
86 return EX_OSERR;
87 }
88
89 if ((cpid = fork()) == -1) {
90 fprintf (stderr, "fork error: %s", strerror (errno));
91 close (pipefd[0]);
92 close (pipefd[1]);
93 return EX_OSERR;
94 }
95
96 if (cpid == 0) {
97 close (pipefd[0]);
98 dup2 (pipefd[1], opts.stream);
99 close (pipefd[1]);
100 close (opts.stream==STDOUT_FILENO?STDERR_FILENO:STDOUT_FILENO);
101
102 if (setsid () == -1) {
103 fprintf (stderr, "setsid error: %s", strerror (errno));
104 _exit (EX_OSERR);
105 }
106
107 execvp (opts.argv[0], opts.argv);
108 fprintf (stderr, "exec error: %s", strerror (errno));
109 _exit (EX_UNAVAILABLE);
110 } else {
111 close (pipefd[1]);
112 fcntl (pipefd[0], F_SETFL, fcntl (pipefd[0], F_GETFL, 0) | O_NONBLOCK);
113
114 gettimeofday (&begin, NULL);
115
116 for (;;) {
117 usleep (20000);
118 memset (buf, 0, BUF_SIZE);
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 (opts.verbose) {
140 fputs (buf, opts.stream==STDERR_FILENO?stderr:stdout);
141 }
142 if (strstr (buf, opts.pattern) != NULL) {
143 printf ("%d\n", cpid);
144 /* create a new child to keep pipe alive (will exit with exec'd program) */
145 if (!fork ()) {
146 while (kill(cpid, 0) != -1 && errno != ESRCH ) sleep (1);
147 close (pipefd[0]);
148 close (pipefd[1]);
149 _exit(0);
150 }
151 close (pipefd[0]);
152 close (pipefd[1]);
153 return EX_OK;
154 }
155
156 if (opts.timeout > 0) {
157 gettimeofday (&now, NULL);
158 timersub (&now, &begin, &diff);
159 if (diff.tv_sec >= opts.timeout) {
160 fprintf (stderr, "Timeout reached. \n");
161 if (opts.kill_sig > 0) kill (cpid, opts.kill_sig);
162 close (pipefd[0]);
163 close (pipefd[1]);
164 return EX_UNAVAILABLE;
165 }
166 }
167 }
168 }
169 }
Imprint / Impressum