fateshare: a plugin for managing child processes
[vpp.git] / src / plugins / fateshare / vpp_fateshare_monitor.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 #include <sys/types.h>
6 #include <sys/wait.h>
7 #include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
8
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 #include <limits.h>
12
13 typedef struct
14 {
15   pid_t pid;
16   char *cmd;
17 } child_record_t;
18
19 int n_children = 0;
20 child_record_t *children = NULL;
21
22 static void
23 child_handler (int sig)
24 {
25   pid_t pid;
26   int status;
27
28   while ((pid = waitpid (-1, &status, WNOHANG)) > 0)
29     {
30       int i;
31       printf ("fateshare: pid %d quit with status %d\n", pid, status);
32       for (i = 0; i < n_children; i++)
33         {
34           if (children[i].pid == pid)
35             {
36               children[i].pid = 0;
37             }
38         }
39     }
40 }
41
42 static void
43 term_handler (int sig)
44 {
45   int i;
46
47   printf ("fateshare: terminating!\n");
48   for (i = 0; i < n_children; i++)
49     {
50       kill (-children[i].pid, SIGTERM);
51     }
52   exit (0);
53 }
54
55 static void
56 hup_handler (int sig)
57 {
58   int i;
59
60   printf ("fateshare: terminating all the child processes!\n");
61   for (i = 0; i < n_children; i++)
62     {
63       kill (-children[i].pid, SIGTERM);
64     }
65 }
66
67 pid_t
68 launch_command (char *scmd, char *logname_base)
69 {
70   pid_t ppid_before_fork = getpid ();
71   pid_t cpid = fork ();
72   if (cpid == -1)
73     {
74       perror ("fork");
75       sleep (1);
76       return 0;
77     }
78   if (cpid)
79     {
80       /* parent */
81       return cpid;
82     }
83
84   /* child */
85   int r = prctl (PR_SET_PDEATHSIG, SIGTERM);
86   if (r == -1)
87     {
88       perror ("prctl");
89       sleep (5);
90       exit (1);
91     }
92   if (getppid () != ppid_before_fork)
93     {
94       sleep (5);
95       exit (1);
96     }
97
98   int r1 = setpgid (getpid (), 0);
99   if (r1 != 0)
100     {
101       perror ("setpgid error");
102       sleep (5);
103       exit (1);
104     }
105
106   int fd = open ("/dev/null", O_RDONLY);
107   if (fd < 0)
108     {
109       sleep (5);
110       exit (1);
111     }
112   while (fd >= 0)
113     {
114       close (fd);
115       fd--;
116     }
117   fd = open ("/dev/null", O_RDONLY);
118   if (fd < 0)
119     {
120       sleep (5);
121       exit (1);
122     }
123   dup2 (fd, 0);
124
125   char logname_stdout[PATH_MAX];
126   char logname_stderr[PATH_MAX];
127
128   snprintf (logname_stdout, PATH_MAX - 1, "%s-stdout.txt", logname_base);
129   snprintf (logname_stderr, PATH_MAX - 1, "%s-stderr.txt", logname_base);
130
131   printf ("LOG STDOUT %s: %s\n", scmd, logname_stdout);
132   printf ("LOG STDERR %s: %s\n", scmd, logname_stderr);
133
134   fd = open ((char *) logname_stdout, O_APPEND | O_RDWR | O_CREAT, 0777);
135   if (fd < 0)
136     {
137       sleep (5);
138       exit (1);
139     }
140   dup2 (fd, 1);
141   fd = open ((char *) logname_stderr, O_APPEND | O_RDWR | O_CREAT, 0777);
142   if (fd < 0)
143     {
144       sleep (5);
145       exit (1);
146     }
147   dup2 (fd, 2);
148
149   char *argv[] = { (char *) scmd, 0 };
150   int res = execv (argv[0], argv);
151   if (res != 0)
152     {
153       perror ("execve");
154     }
155   sleep (10);
156
157   exit (42);
158 }
159
160 int
161 main (int argc, char **argv)
162 {
163   pid_t ppid = getppid ();
164   int i = 0;
165   if (argc < 3)
166     {
167       printf ("usage: %s <parent_pid> <logfile-basename>\n", argv[0]);
168       exit (1);
169     }
170   char *errptr = 0;
171   pid_t parent_pid = strtoll (argv[1], &errptr, 10);
172   char *logname_base = argv[2];
173
174   printf ("DEBUG: pid %d starting for parent pid %d\n", getpid (), ppid);
175   printf ("DEBUG: parent pid: %d\n", parent_pid);
176   printf ("DEBUG: base log name: %s\n", logname_base);
177   if (*errptr)
178     {
179       printf ("%s is not a valid parent pid\n", errptr);
180       exit (2);
181     }
182
183   int r = prctl (PR_SET_PDEATHSIG, SIGTERM);
184   if (r == -1)
185     {
186       perror (0);
187       exit (1);
188     }
189
190   /* Establish handler. */
191   struct sigaction sa;
192   sigemptyset (&sa.sa_mask);
193   sa.sa_flags = 0;
194   sa.sa_handler = child_handler;
195
196   sigaction (SIGCHLD, &sa, NULL);
197
198   sigemptyset (&sa.sa_mask);
199   sa.sa_flags = 0;
200   sa.sa_handler = term_handler;
201
202   sigaction (SIGTERM, &sa, NULL);
203
204   sigemptyset (&sa.sa_mask);
205   sa.sa_flags = 0;
206   sa.sa_handler = hup_handler;
207
208   sigaction (SIGHUP, &sa, NULL);
209
210   if (getppid () != parent_pid)
211     {
212       printf ("parent process unexpectedly finished\n");
213       exit (3);
214     }
215
216   argc -= 3; /* skip over argv0, ppid, and log base */
217   argv += 3;
218
219   n_children = argc;
220   printf ("DEBUG: total %d children\n", n_children);
221   children = calloc (n_children, sizeof (children[0]));
222   for (i = 0; i < n_children; i++)
223     {
224       /* argv persists, so we can just use that pointer */
225       children[i].cmd = argv[i];
226       children[i].pid = launch_command (children[i].cmd, logname_base);
227       printf ("DEBUG: child %d (%s): initial launch pid %d\n", i,
228               children[i].cmd, children[i].pid);
229     }
230
231   while (1)
232     {
233       sleep (1);
234       pid_t curr_ppid = getppid ();
235       printf ("pid: %d, current ppid %d, original ppid %d\n", getpid (),
236               curr_ppid, ppid);
237       if (curr_ppid != ppid)
238         {
239           printf ("current ppid %d != original ppid %d - force quit\n",
240                   curr_ppid, ppid);
241           fflush (stdout);
242           exit (1);
243         }
244       int restarted = 0;
245       for (i = 0; i < n_children; i++)
246         {
247           if (children[i].pid == 0)
248             {
249               printf ("child %s exited, restarting\n", children[i].cmd);
250               restarted = 1;
251               children[i].pid = launch_command (children[i].cmd, logname_base);
252             }
253         }
254       if (restarted)
255         {
256           sleep (1);
257         }
258
259       fflush (stdout);
260     }
261 }