in program help text
[forkaftergrep.git] / fag.c
CommitLineData
4f83e12d 1/* forkaftergrep (C) 2017 Tobias Girstmair, GPLv3 */
d8642d91 2//TODO: supply user arguments to grep
f27db2a6 3//demo: ./fag -e -V "MPD server running at" mopidy
4f83e12d 4
5#define _XOPEN_SOURCE 500
34f76df0 6#define _DEFAULT_SOURCE
4f83e12d 7#include <errno.h>
8#include <fcntl.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sysexits.h>
13#include <sys/time.h>
14#include <sys/wait.h>
15#include <unistd.h>
16#include "fag.h"
17
ff6565ef 18struct opt opts = {0, 0, 0, NULL, NULL, STDOUT_FILENO};
4f83e12d 19
20int main (int argc, char** argv) {
21 int opt;
22 opterr = 0;
23
24 /* the `+' forces getopt to stop at the first non-option */
ff6565ef 25 while ((opt = getopt (argc, argv, "+t:k::eVhv")) != -1) {
4f83e12d 26 switch (opt) {
27 case 't':
28 opts.timeout = atoi (optarg);
29 break;
30 case 'k':
31 opts.kill_sig = optarg ? atoi (optarg) : SIGTERM;
32 break;
33 case 'e':
34 opts.stream = STDERR_FILENO;
35 break;
ff6565ef 36 case 'V':
37 opts.verbose = 1;
38 break;
4f83e12d 39 case 'h':
40 fprintf (stderr, VERTEXT USAGE
41 "Options:\n"
42 "\t-t N\ttimeout after N seconds\n"
43 "\t-k [M]\tsend signal M to child after timeout (default: 15/SIGTERM)\n"
329d9605 44 "\t-e\tgrep on stderr instead of stdout\n"
45 "\t-V\tbe verbose; print PROGRAM's stdout/stderr to stderr\n", argv[0]);
4f83e12d 46 return EX_OK;
47 case 'v':
48 fprintf (stderr, VERTEXT);
49 return EX_OK;
50 default:
51 fprintf (stderr, "Unrecognized option: %c\n" USAGE, optopt, argv[0]);
52 return EX_USAGE;
53 }
54 }
55
56 /* the first non-option argument is the search string */
57 if (optind < argc) {
58 opts.pattern = argv[optind++];
59 } else {
60 fprintf (stderr, USAGE "(Missing PATTERN)\n", argv[0]);
61 return EX_USAGE;
62 }
63
64 /* the remaining are the program to be run */
65 if (optind < argc) {
66 opts.argv = &(argv[optind]);
67 } else {
68 fprintf (stderr, USAGE "(Missing PROGRAM)\n", argv[0]);
69 return EX_USAGE;
70 }
71
72 int retval = fork_after_grep (opts);
73
74 return retval;
75}
76
77int fork_after_grep (struct opt opts) {
4f83e12d 78 int pipefd[2];
79 pid_t cpid;
80 int status;
81
82 char buf[BUF_SIZE];
83 int nbytes;
84
85 struct timeval begin, now, diff;
86
87 if (pipe(pipefd) == -1) {
f27db2a6 88 fprintf (stderr, "pipe error (userprog)\n");
4f83e12d 89 return EX_OSERR;
90 }
91
92 if ((cpid = fork()) == -1) {
f27db2a6 93 fprintf (stderr, "fork error (userprog): %s", strerror (errno));
4f83e12d 94 close (pipefd[0]);
95 close (pipefd[1]);
96 return EX_OSERR;
97 }
98
99 if (cpid == 0) {
100 close (pipefd[0]);
101 dup2 (pipefd[1], opts.stream);
102 close (pipefd[1]);
103 close (opts.stream==STDOUT_FILENO?STDERR_FILENO:STDOUT_FILENO);
104
105 if (setsid () == -1) {
f27db2a6 106 fprintf (stderr, "setsid error (userprog): %s", strerror (errno));
4f83e12d 107 _exit (EX_OSERR);
108 }
109
110 execvp (opts.argv[0], opts.argv);
f27db2a6 111 fprintf (stderr, "exec error (userprog): %s", strerror (errno));
4f83e12d 112 _exit (EX_UNAVAILABLE);
113 } else {
52503080 114 pid_t grep_cpid;
f27db2a6 115 int pipefd_togrep[2];
116 int grep_status;
117 /* `-q': don't print anything; exit with 0 on match; with 1 on error */
d8642d91 118 char* grepargv[] = {"grep", "-q", opts.pattern, NULL};
52503080 119
4f83e12d 120 close (pipefd[1]);
121 fcntl (pipefd[0], F_SETFL, fcntl (pipefd[0], F_GETFL, 0) | O_NONBLOCK);
122
d8642d91 123 gettimeofday (&begin, NULL); /* for timeout */
4f83e12d 124
f27db2a6 125 if (pipe(pipefd_togrep) == -1) {
126 fprintf (stderr, "pipe error (grep)\n");
d8642d91 127 close (pipefd[0]);
128 close (pipefd[1]);
52503080 129 return EX_OSERR;
130 }
f27db2a6 131
52503080 132 if ((grep_cpid = fork()) == -1) {
f27db2a6 133 fprintf (stderr, "fork error (grep): %s", strerror (errno));
52503080 134 close (pipefd[0]);
135 close (pipefd[1]);
d8642d91 136 close (pipefd_togrep[0]);
137 close (pipefd_togrep[1]);
52503080 138 return EX_OSERR;
139 }
f27db2a6 140
52503080 141 if (grep_cpid == 0) {
f27db2a6 142 close (pipefd_togrep[1]);
143 dup2 (pipefd_togrep[0], STDIN_FILENO);
144 close (pipefd_togrep[0]);
145
146 close (STDERR_FILENO);
147 close (STDOUT_FILENO);
148
52503080 149 execvp (grepargv[0], grepargv);
f27db2a6 150 fprintf (stderr, "exec error (grep): %s", strerror (errno));
52503080 151 _exit (EX_UNAVAILABLE);
152 } else {
d8642d91 153 close (pipefd_togrep[0]);
52503080 154 for (;;) {
155 usleep (20000);
d8642d91 156 memset (buf, 0, BUF_SIZE); /* necessary for opts.verbose */
52503080 157 nbytes = read (pipefd[0], buf, BUF_SIZE);
158 if (nbytes == -1) {
159 switch (errno) {
160 case EAGAIN:
f27db2a6 161 break;
52503080 162 default:
f27db2a6 163 fprintf (stderr, "read error (userprog): %s", strerror (errno));
52503080 164 close (pipefd[0]);
165 close (pipefd[1]);
d8642d91 166 close (pipefd_togrep[1]);
167 //TODO: kill grep?
52503080 168 return EX_IOERR;
169 }
170 } else if (nbytes == 0) {
f27db2a6 171 fprintf (stderr, "Child program exited prematurely (userprog).\n");
4f83e12d 172 close (pipefd[0]);
173 close (pipefd[1]);
d8642d91 174 close (pipefd_togrep[1]);
175 //TODO: kill grep?
52503080 176 if (waitpid (cpid, &status, WNOHANG) > 0 && WIFEXITED (status)) {
177 return WEXITSTATUS (status);
178 }
179 return EX_UNAVAILABLE;
f27db2a6 180 } else {
181 /* have new userprog-data, send it to grep */
182 if (opts.verbose) {
183 fputs (buf, stderr);
184 }
185
186 write(pipefd_togrep[1], buf, strlen(buf));
4f83e12d 187 }
f27db2a6 188
189 if (waitpid (grep_cpid, &grep_status, WNOHANG) > 0 && WIFEXITED (grep_status)) {
d8642d91 190 close (pipefd_togrep[1]);
191
f27db2a6 192 if (WEXITSTATUS(grep_status) == 0) {
193 /* grep exited with match found */
194 printf ("%d\n", cpid);
d8642d91 195
f27db2a6 196 /* create a new child to keep pipe alive (will exit with exec'd program) */
197 if (!fork ()) {
198 while (kill(cpid, 0) != -1 && errno != ESRCH ) sleep (1);
199 close (pipefd[0]);
200 close (pipefd[1]);
201 _exit(0);
202 }
203 close (pipefd[0]);
204 close (pipefd[1]);
205 return EX_OK;
206 } else {
207 /* grep exited due to an error */
208 fprintf (stderr, "grep exited due to an error.");
52503080 209 close (pipefd[0]);
210 close (pipefd[1]);
f27db2a6 211 close (pipefd_togrep[1]);
212 return EX_IOERR;
52503080 213 }
4f83e12d 214 }
4f83e12d 215
52503080 216 if (opts.timeout > 0) {
217 gettimeofday (&now, NULL);
218 timersub (&now, &begin, &diff);
219 if (diff.tv_sec >= opts.timeout) {
220 fprintf (stderr, "Timeout reached. \n");
221 if (opts.kill_sig > 0) kill (cpid, opts.kill_sig);
222 close (pipefd[0]);
223 close (pipefd[1]);
d8642d91 224 close (pipefd_togrep[1]);
225 //TODO: kill grep?
52503080 226 return EX_UNAVAILABLE;
227 }
4f83e12d 228 }
229 }
230 }
231 }
232}
Imprint / Impressum