start with integrating actual grep
[forkaftergrep.git] / fag.c
CommitLineData
4f83e12d 1/* forkaftergrep (C) 2017 Tobias Girstmair, GPLv3 */
2
3#define _XOPEN_SOURCE 500
34f76df0 4#define _DEFAULT_SOURCE
4f83e12d 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
ff6565ef 16struct opt opts = {0, 0, 0, NULL, NULL, STDOUT_FILENO};
4f83e12d 17
18int main (int argc, char** argv) {
19 int opt;
20 opterr = 0;
21
22 /* the `+' forces getopt to stop at the first non-option */
ff6565ef 23 while ((opt = getopt (argc, argv, "+t:k::eVhv")) != -1) {
4f83e12d 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;
ff6565ef 34 case 'V':
35 opts.verbose = 1;
36 break;
4f83e12d 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
74int fork_after_grep (struct opt opts) {
4f83e12d 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 {
52503080 111 pid_t grep_cpid;
112 int grep_pipefd[2];
113 //char grep_buf[BUF_SIZE];
114 //int grep_nbytes;
115 //char* grepargv[] = {"cat", NULL};
116 char* grepargv[] = {"grep", opts.pattern, NULL};//"-E", //TODO: supply params (-E, -P, etc.) to grep from command line
117
4f83e12d 118 close (pipefd[1]);
119 fcntl (pipefd[0], F_SETFL, fcntl (pipefd[0], F_GETFL, 0) | O_NONBLOCK);
120
121 gettimeofday (&begin, NULL);
122
52503080 123 if (pipe(grep_pipefd) == -1) {
124 fprintf (stderr, "mygrep - pipe error\n");
125 return EX_OSERR;
126 }
127 if ((grep_cpid = fork()) == -1) {
128 fprintf (stderr, "mygrep - fork error: %s", strerror (errno));
129 close (pipefd[0]);
130 close (pipefd[1]);
131 return EX_OSERR;
132 }
133 if (grep_cpid == 0) {
134 close(grep_pipefd[1]);
135 dup2(grep_pipefd[0], STDIN_FILENO);
136 close(grep_pipefd[0]);
137 close(STDERR_FILENO);
138 execvp (grepargv[0], grepargv);
139 fprintf (stderr, "mygrep - exec error: %s", strerror (errno));
140 _exit (EX_UNAVAILABLE);
141 } else {
142 for (;;) {
143 usleep (20000);
144 memset (buf, 0, BUF_SIZE);
145 nbytes = read (pipefd[0], buf, BUF_SIZE);
146 if (nbytes == -1) {
147 switch (errno) {
148 case EAGAIN:
149 continue;
150 default:
151 fprintf (stderr, "read error: %s", strerror (errno));
152 close (pipefd[0]);
153 close (pipefd[1]);
154 return EX_IOERR;
155 }
156 } else if (nbytes == 0) {
157 fprintf (stderr, "Child program exited prematurely.\n");
4f83e12d 158 close (pipefd[0]);
159 close (pipefd[1]);
52503080 160 if (waitpid (cpid, &status, WNOHANG) > 0 && WIFEXITED (status)) {
161 return WEXITSTATUS (status);
162 }
163 return EX_UNAVAILABLE;
4f83e12d 164 }
52503080 165 if (opts.verbose) {
166 fputs (buf, opts.stream==STDERR_FILENO?stderr:stdout);
4f83e12d 167 }
52503080 168 write(grep_pipefd[1], buf, strlen(buf));/*************/
169 if (strstr (buf, opts.pattern) != NULL) {
170 printf ("%d\n", cpid);
171 /* create a new child to keep pipe alive (will exit with exec'd program) */
172 if (!fork ()) {
173 while (kill(cpid, 0) != -1 && errno != ESRCH ) sleep (1);
174 close (pipefd[0]);
175 close (pipefd[1]);
176 _exit(0);
177 }
4f83e12d 178 close (pipefd[0]);
179 close (pipefd[1]);
52503080 180 return EX_OK;
4f83e12d 181 }
4f83e12d 182
52503080 183 if (opts.timeout > 0) {
184 gettimeofday (&now, NULL);
185 timersub (&now, &begin, &diff);
186 if (diff.tv_sec >= opts.timeout) {
187 fprintf (stderr, "Timeout reached. \n");
188 if (opts.kill_sig > 0) kill (cpid, opts.kill_sig);
189 close (pipefd[0]);
190 close (pipefd[1]);
191 return EX_UNAVAILABLE;
192 }
4f83e12d 193 }
194 }
195 }
196 }
197}
Imprint / Impressum