ip: mark IP_ADDRESS_DUMP as mp-safe
[vpp.git] / src / plugins / fateshare / fateshare.c
1 /*
2  * fateshare.c - skeleton vpp engine plug-in
3  *
4  * Copyright (c) 2022 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <vppinfra/unix.h>
21 #include <fateshare/fateshare.h>
22
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25 #include <vpp/app/version.h>
26 #include <stdbool.h>
27
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #ifdef __linux__
31 #include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
32 #else
33 #include <sys/procctl.h>
34 #endif /* __linux__ */
35 #include <limits.h>
36
37 fateshare_main_t fateshare_main;
38
39 /* Action function shared between message handler and debug CLI */
40
41 static void
42 child_handler (int sig)
43 {
44   pid_t pid;
45   int status;
46   fateshare_main_t *kmp = &fateshare_main;
47
48   while ((pid = waitpid (-1, &status, WNOHANG)) > 0)
49     {
50       if (pid == kmp->monitor_pid)
51         {
52           clib_warning ("Monitor child %d exited with status %d!", pid,
53                         status);
54           kmp->vlib_main->main_loop_exit_now = 1;
55         }
56       else
57         {
58           clib_warning ("child %d exited with status %d!", pid, status);
59         }
60     }
61 }
62
63 clib_error_t *
64 launch_monitor (fateshare_main_t *kmp)
65 {
66   clib_error_t *error = 0;
67   pid_t ppid_before_fork = getpid ();
68   pid_t cpid = fork ();
69   if (cpid == -1)
70     {
71       perror (0);
72       error = clib_error_return (0, "can not fork");
73       goto done;
74     }
75   clib_warning ("fateshare about to launch monitor %v.", kmp->monitor_cmd);
76   int logfd =
77     open ((char *) kmp->monitor_logfile, O_APPEND | O_RDWR | O_CREAT, 0777);
78   if (logfd < 0)
79     {
80       error = clib_error_return (0, "can not open log file");
81       goto done;
82     }
83   if (cpid)
84     {
85       /* parent */
86       kmp->monitor_pid = cpid;
87       close (logfd);
88       return 0;
89     }
90   else
91     {
92       dup2 (logfd, 1);
93       dup2 (logfd, 2);
94 #ifdef __linux__
95       int r = prctl (PR_SET_PDEATHSIG, SIGTERM);
96       if (r == -1)
97         {
98           perror (0);
99           exit (1);
100         }
101 #else
102       int r, s = SIGTERM;
103
104       r = procctl (P_PID, 0, PROC_PDEATHSIG_CTL, &s);
105       if (r == -1)
106         {
107           perror (0);
108           exit (1);
109         }
110 #endif /* __linux__ */
111       pid_t current_ppid = getppid ();
112       if (current_ppid != ppid_before_fork)
113         {
114           fprintf (stderr, "parent pid changed while starting (%d => %d)\n",
115                    ppid_before_fork, current_ppid);
116           if (current_ppid == 1)
117             {
118               fprintf (stderr, "exiting.\n");
119               exit (1);
120             }
121         }
122
123       int r1 = setpgid (getpid (), 0);
124       if (r1 != 0)
125         {
126           perror ("setpgid error");
127           exit (1);
128         }
129
130       u8 *scmd = format (0, "%v\0", kmp->monitor_cmd);
131       u8 *logfile_base = format (0, "%v\0", kmp->monitor_logfile);
132       int fd = logfd - 1;
133       while (fd > 2)
134         {
135           close (fd);
136           fd--;
137         }
138
139       fd = open ("/dev/null", O_RDONLY);
140       if (fd < 0)
141         {
142           exit (1);
143         }
144       dup2 (fd, 0);
145
146       char *ppid_str = (char *) format (0, "%lld\0", current_ppid);
147
148       char **argv = 0;
149       vec_validate (argv, vec_len (kmp->commands) + 3 - 1);
150       argv[0] = (void *) scmd;
151       argv[1] = ppid_str;
152       argv[2] = (char *) logfile_base;
153       int i;
154       vec_foreach_index (i, kmp->commands)
155         {
156           argv[3 + i] = (char *) kmp->commands[i];
157         }
158
159       int res = execv (argv[0], argv);
160       clib_warning ("ERROR during execve: %d", res);
161       perror ("execve");
162
163       exit (0);
164     }
165 done:
166
167   return error;
168 }
169
170 static clib_error_t *
171 fateshare_config (vlib_main_t *vm, unformat_input_t *input)
172 {
173   fateshare_main_t *fmp = &fateshare_main;
174   u8 *command = 0;
175   u8 **new_command = 0;
176   clib_error_t *error = 0;
177
178   /* unix config may make vpp fork, we want to run after that. */
179   if ((error = vlib_call_config_function (vm, unix_config)))
180     return error;
181
182   /* Defaults */
183   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
184     {
185       if (unformat (input, "monitor %s", &fmp->monitor_cmd))
186         {
187           clib_warning ("setting monitor to %v", fmp->monitor_cmd);
188         }
189       else if (unformat (input, "logfile %s", &fmp->monitor_logfile))
190         {
191           clib_warning ("setting logfile to %v", fmp->monitor_logfile);
192         }
193       else if (unformat (input, "command %s", &command))
194         {
195           vec_add2 (fmp->commands, new_command, 1);
196           *new_command = command;
197         }
198       else
199         return clib_error_return (0, "unknown input `%U'",
200                                   format_unformat_error, input);
201     }
202
203   vec_add2 (fmp->commands, new_command, 1);
204   *new_command = 0;
205
206   /* Establish handler. */
207   struct sigaction sa;
208   sigemptyset (&sa.sa_mask);
209   sa.sa_flags = 0;
210   sa.sa_handler = child_handler;
211
212   sigaction (SIGCHLD, &sa, NULL);
213
214   if (fmp->monitor_cmd == 0)
215     {
216       char *p;
217       u8 *path;
218
219       /* find executable path */
220       path = os_get_exec_path ();
221
222       if (path == 0)
223         return clib_error_return (
224           0, "could not get exec path - set monitor manually");
225
226       /* add null termination */
227       vec_add1 (path, 0);
228
229       /* strip filename */
230       if ((p = strrchr ((char *) path, '/')) == 0)
231         {
232           vec_free (path);
233           return clib_error_return (
234             0, "could not determine vpp directory - set monitor manually");
235         }
236       *p = 0;
237
238       fmp->monitor_cmd = format (0, "%s/vpp_fateshare_monitor\0", path);
239       vec_free (path);
240     }
241   if (fmp->monitor_logfile == 0)
242     {
243       fmp->monitor_logfile =
244         format (0, "/tmp/vpp-fateshare-monitor-log.txt\0");
245     }
246   error = launch_monitor (fmp);
247
248   return error;
249 }
250
251 clib_error_t *
252 fateshare_init (vlib_main_t *vm)
253 {
254   fateshare_main_t *kmp = &fateshare_main;
255   clib_error_t *error = 0;
256
257   kmp->vlib_main = vm;
258
259   return error;
260 }
261
262 static clib_error_t *
263 fateshare_send_hup_fn (vlib_main_t *vm, unformat_input_t *input,
264                        vlib_cli_command_t *cmd)
265 {
266   clib_error_t *error = 0;
267   fateshare_main_t *kmp = &fateshare_main;
268
269   if (kmp->monitor_pid)
270     {
271       int rc = kill (kmp->monitor_pid, SIGHUP);
272       if (rc)
273         {
274           error = clib_error_return (
275             0, "can not send signal to monitor process: %s", strerror (errno));
276         }
277     }
278   else
279     {
280       error = clib_error_return (0, "can not find monitor process");
281     }
282
283   return error;
284 }
285
286 VLIB_EARLY_CONFIG_FUNCTION (fateshare_config, "fateshare");
287
288 VLIB_INIT_FUNCTION (fateshare_init);
289
290 VLIB_CLI_COMMAND (fateshare_restart_process_command, static) = {
291   .path = "fateshare restart-processes",
292   .short_help = "restart dependent processes",
293   .function = fateshare_send_hup_fn,
294 };
295
296 VLIB_PLUGIN_REGISTER () = {
297   .version = VPP_BUILD_VER,
298   .description = "Run child processes which will share fate with VPP, restart "
299                  "them if they quit",
300   .default_disabled = 1,
301 };
302
303 /*
304  * fd.io coding-style-patch-verification: ON
305  *
306  * Local Variables:
307  * eval: (c-set-style "gnu")
308  * End:
309  */