vppinfra: Add method for getting current executable name
[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 #include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
31 #include <limits.h>
32
33 fateshare_main_t fateshare_main;
34
35 /* Action function shared between message handler and debug CLI */
36
37 static void
38 child_handler (int sig)
39 {
40   pid_t pid;
41   int status;
42   fateshare_main_t *kmp = &fateshare_main;
43
44   while ((pid = waitpid (-1, &status, WNOHANG)) > 0)
45     {
46       if (pid == kmp->monitor_pid)
47         {
48           clib_warning ("Monitor child %d exited with status %d!", pid,
49                         status);
50           kmp->vlib_main->main_loop_exit_now = 1;
51         }
52       else
53         {
54           clib_warning ("child %d exited with status %d!", pid, status);
55         }
56     }
57 }
58
59 clib_error_t *
60 launch_monitor (fateshare_main_t *kmp)
61 {
62   clib_error_t *error = 0;
63   pid_t ppid_before_fork = getpid ();
64   pid_t cpid = fork ();
65   if (cpid == -1)
66     {
67       perror (0);
68       error = clib_error_return (0, "can not fork");
69       goto done;
70     }
71   clib_warning ("fateshare about to launch monitor %v.", kmp->monitor_cmd);
72   int logfd =
73     open ((char *) kmp->monitor_logfile, O_APPEND | O_RDWR | O_CREAT, 0777);
74   if (logfd < 0)
75     {
76       error = clib_error_return (0, "can not open log file");
77       goto done;
78     }
79   if (cpid)
80     {
81       /* parent */
82       kmp->monitor_pid = cpid;
83       close (logfd);
84       return 0;
85     }
86   else
87     {
88       dup2 (logfd, 1);
89       dup2 (logfd, 2);
90       int r = prctl (PR_SET_PDEATHSIG, SIGTERM);
91       if (r == -1)
92         {
93           perror (0);
94           exit (1);
95         }
96       pid_t current_ppid = getppid ();
97       if (current_ppid != ppid_before_fork)
98         {
99           fprintf (stderr, "parent pid changed while starting (%d => %d)\n",
100                    ppid_before_fork, current_ppid);
101           if (current_ppid == 1)
102             {
103               fprintf (stderr, "exiting.\n");
104               exit (1);
105             }
106         }
107
108       int r1 = setpgid (getpid (), 0);
109       if (r1 != 0)
110         {
111           perror ("setpgid error");
112           exit (1);
113         }
114
115       u8 *scmd = format (0, "%v\0", kmp->monitor_cmd);
116       u8 *logfile_base = format (0, "%v\0", kmp->monitor_logfile);
117       int fd = logfd - 1;
118       while (fd > 2)
119         {
120           close (fd);
121           fd--;
122         }
123
124       fd = open ("/dev/null", O_RDONLY);
125       if (fd < 0)
126         {
127           exit (1);
128         }
129       dup2 (fd, 0);
130
131       char *ppid_str = (char *) format (0, "%lld\0", current_ppid);
132
133       char **argv = 0;
134       vec_validate (argv, vec_len (kmp->commands) + 3 - 1);
135       argv[0] = (void *) scmd;
136       argv[1] = ppid_str;
137       argv[2] = (char *) logfile_base;
138       int i;
139       vec_foreach_index (i, kmp->commands)
140         {
141           argv[3 + i] = (char *) kmp->commands[i];
142         }
143
144       int res = execv (argv[0], argv);
145       clib_warning ("ERROR during execve: %d", res);
146       perror ("execve");
147
148       exit (0);
149     }
150 done:
151
152   return error;
153 }
154
155 static clib_error_t *
156 fateshare_config (vlib_main_t *vm, unformat_input_t *input)
157 {
158   fateshare_main_t *fmp = &fateshare_main;
159   u8 *command = 0;
160   u8 **new_command = 0;
161   clib_error_t *error = 0;
162
163   /* unix config may make vpp fork, we want to run after that. */
164   if ((error = vlib_call_config_function (vm, unix_config)))
165     return error;
166
167   /* Defaults */
168   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
169     {
170       if (unformat (input, "monitor %s", &fmp->monitor_cmd))
171         {
172           clib_warning ("setting monitor to %v", fmp->monitor_cmd);
173         }
174       else if (unformat (input, "logfile %s", &fmp->monitor_logfile))
175         {
176           clib_warning ("setting logfile to %v", fmp->monitor_logfile);
177         }
178       else if (unformat (input, "command %s", &command))
179         {
180           vec_add2 (fmp->commands, new_command, 1);
181           *new_command = command;
182         }
183       else
184         return clib_error_return (0, "unknown input `%U'",
185                                   format_unformat_error, input);
186     }
187
188   vec_add2 (fmp->commands, new_command, 1);
189   *new_command = 0;
190
191   /* Establish handler. */
192   struct sigaction sa;
193   sigemptyset (&sa.sa_mask);
194   sa.sa_flags = 0;
195   sa.sa_handler = child_handler;
196
197   sigaction (SIGCHLD, &sa, NULL);
198
199   if (fmp->monitor_cmd == 0)
200     {
201       char *p;
202       u8 *path;
203
204       /* find executable path */
205       path = os_get_exec_path ();
206
207       if (path == 0)
208         return clib_error_return (
209           0, "could not get exec path - set monitor manually");
210
211       /* add null termination */
212       vec_add1 (path, 0);
213
214       /* strip filename */
215       if ((p = strrchr ((char *) path, '/')) == 0)
216         {
217           vec_free (path);
218           return clib_error_return (
219             0, "could not determine vpp directory - set monitor manually");
220         }
221       *p = 0;
222
223       fmp->monitor_cmd = format (0, "%s/vpp_fateshare_monitor\0", path);
224       vec_free (path);
225     }
226   if (fmp->monitor_logfile == 0)
227     {
228       fmp->monitor_logfile =
229         format (0, "/tmp/vpp-fateshare-monitor-log.txt\0");
230     }
231   error = launch_monitor (fmp);
232
233   return error;
234 }
235
236 clib_error_t *
237 fateshare_init (vlib_main_t *vm)
238 {
239   fateshare_main_t *kmp = &fateshare_main;
240   clib_error_t *error = 0;
241
242   kmp->vlib_main = vm;
243
244   return error;
245 }
246
247 static clib_error_t *
248 fateshare_send_hup_fn (vlib_main_t *vm, unformat_input_t *input,
249                        vlib_cli_command_t *cmd)
250 {
251   clib_error_t *error = 0;
252   fateshare_main_t *kmp = &fateshare_main;
253
254   if (kmp->monitor_pid)
255     {
256       int rc = kill (kmp->monitor_pid, SIGHUP);
257       if (rc)
258         {
259           error = clib_error_return (
260             0, "can not send signal to monitor process: %s", strerror (errno));
261         }
262     }
263   else
264     {
265       error = clib_error_return (0, "can not find monitor process");
266     }
267
268   return error;
269 }
270
271 VLIB_EARLY_CONFIG_FUNCTION (fateshare_config, "fateshare");
272
273 VLIB_INIT_FUNCTION (fateshare_init);
274
275 VLIB_CLI_COMMAND (fateshare_restart_process_command, static) = {
276   .path = "fateshare restart-processes",
277   .short_help = "restart dependent processes",
278   .function = fateshare_send_hup_fn,
279 };
280
281 VLIB_PLUGIN_REGISTER () = {
282   .version = VPP_BUILD_VER,
283   .description = "Run child processes which will share fate with VPP, restart "
284                  "them if they quit",
285   .default_disabled = 1,
286 };
287
288 /*
289  * fd.io coding-style-patch-verification: ON
290  *
291  * Local Variables:
292  * eval: (c-set-style "gnu")
293  * End:
294  */