d94ddf8950570875b881175f6b3a7f5b5c8281c2
[vpp.git] / vpp-api-test / vat / restart.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <stdio.h>
17 #include <time.h>
18 #include <sys/types.h>
19 #include <signal.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include <vppinfra/clib.h>
24 #include <vppinfra/vec.h>
25 #include <vppinfra/hash.h>
26 #include <svmdb.h>
27 #include <vppinfra/format.h>
28 #include <vppinfra/error.h>
29 #include <vppinfra/time.h>
30 #include <vppinfra/macros.h>
31
32 int restart_main_fn (unformat_input_t * i)
33 {
34   int verbose = 0;
35   int old_pid;
36   int wait;
37   u8 * chroot_path = 0;
38   svmdb_client_t * svmdb_client;
39   volatile pid_t *pidp;
40   struct stat statb;
41   ino_t old_inode;
42   int sleeps;
43
44   struct timespec _req, *req = &_req;
45   struct timespec _rem, *rem = &_rem;
46
47   if (geteuid())
48     clib_error ("vpp_restart: must be root...");
49
50   while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
51     {
52       if (unformat (i, "verbose") || unformat (i, "v"))
53         verbose = 1;
54       else if (unformat (i, "chroot %s", &chroot_path))
55         ;
56       else
57         {
58           clib_error ("unknown input `%U'", format_unformat_error, i);
59           return 1;
60         }
61     }
62
63   /* 
64    * Step 1: look up the current VPP pid in the shared-memory database 
65    */
66   svmdb_client = svmdb_map_chroot ((char *) chroot_path);
67
68   pidp = svmdb_local_get_variable_reference (svmdb_client,
69                                              SVMDB_NAMESPACE_VEC, 
70                                              "vpp_pid");
71   if (pidp == 0)
72     {
73       clib_error ("'vpp_pid' svm variable not found, vpp has never run?");
74       return 2;
75     }
76
77   /* Spin for up to 10 seconds for vpp to start */
78   for (wait = 0; wait < 1000; wait++)
79     {
80       req->tv_sec = 0;
81       req->tv_nsec = 10000*1000;  /* 10 ms */
82       while (nanosleep(req, rem) < 0)
83         *req = *rem;
84       
85       if (*pidp) 
86         goto found2;
87     }
88
89   clib_error ("VPP not runnning...");
90   return 3;
91   
92  found2:
93
94   old_pid = *pidp;
95   
96   /*
97    * Step 2: sanity check the pid we discovered
98    */
99   if (verbose)
100     fformat(stdout, "Sanity check current vpp pid %d\n", old_pid);
101
102   if (kill (old_pid, 0) < 0)
103     {
104       svmdb_unmap (svmdb_client);
105       clib_error ("vpp current pid %d not running...", old_pid);
106       return 2;
107     }
108   
109   if (verbose)
110     fformat(stdout, "Sanity check vpp pid %d OK\n", old_pid);
111
112   /* 
113    * Step 3: figure out the current vpp <--> client shared-VM file 
114    * inode number 
115    */
116   if (stat("/dev/shm/vpe-api", &statb) < 0)
117     {
118       clib_unix_error ("stat fail");
119       return 4;
120     }
121
122   old_inode = statb.st_ino;
123
124   if (verbose)
125     fformat(stdout, "Old inode %u\n", old_inode);
126
127   /* Note: restart wipes out the shared VM database*/
128   svmdb_unmap (svmdb_client);
129
130   /*
131    * Step 4: send SIGTERM to vpp.
132    * systemd et al. will restart vpp after wiping out the shared-VM
133    * database and (crucially) the shared API messaging segment
134    */
135
136   if (kill (old_pid, SIGTERM) < 0)
137     {
138       clib_unix_error ("SIGTERM fail");
139       return 3;
140     }
141
142   sleeps = 0;
143
144   /* 
145    * Step 5: wait up to 15 seconds for a new incarnation of 
146    * the shared-VM API segment to appear.
147    */
148   for (wait = 0; wait < 150; wait++)
149     {
150       if ((stat("/dev/shm/vpe-api", &statb) < 0)
151           || statb.st_ino == old_inode)
152         {
153           req->tv_sec = 0;
154           req->tv_nsec = 100000*1000;  /* 100 ms */
155           while (nanosleep(req, rem) < 0)
156             *req = *rem;
157           sleeps++;
158         }
159       else
160         goto new_inode;
161     }
162
163   clib_error ("Timeout waiting for new inode to appear...");
164   return 5;
165
166  new_inode:
167   if (verbose && sleeps > 0)
168     fformat(stdout, "Inode sleeps %d\n", sleeps);
169
170   if (verbose)
171     fformat(stdout, "New inode %u\n", statb.st_ino);
172
173   /* 
174    * Step 6: remap the SVM database 
175    */
176   svmdb_client = svmdb_map_chroot ((char *) chroot_path);
177   
178   pidp = svmdb_local_get_variable_reference (svmdb_client,
179                                              SVMDB_NAMESPACE_VEC, 
180                                              "vpp_pid");
181   if (pidp == 0)
182     {
183       clib_error ("post_restart: 'vpp_pid' svm variable not found,"
184                   "vpp did not restart?");
185       return 2;
186     }
187
188   sleeps = 0;
189
190   /* 
191    * Step 7: wait for vpp to publish its new PID
192    */
193
194   /* Spin for up to 15 seconds */
195   for (wait = 0; wait < 150; wait++)
196     {
197       if (*pidp && (*pidp != old_pid))
198         goto restarted;
199       req->tv_sec = 0;
200       req->tv_nsec = 100000*1000;  /* 100 ms */
201       while (nanosleep(req, rem) < 0)
202         *req = *rem;
203       sleeps++;
204     }
205   
206   clib_error ("Timeout waiting for vpp to publish pid after restart...");
207   return 4;
208
209  restarted:  
210
211   /* Done... */
212   
213   if (verbose && sleeps)
214     fformat(stdout, "pid sleeps %d\n", sleeps);
215
216   if (verbose)
217     fformat (stdout, "New PID %d... Restarted...\n", *pidp);
218   
219   svmdb_unmap (svmdb_client);
220   return 0;
221 }
222
223 int main (int argc, char **argv)
224 {
225   unformat_input_t i;
226   int ret;
227
228   clib_mem_init (0, 64ULL<<20);
229
230   unformat_init_command_line (&i, argv);
231   ret = restart_main_fn (&i);
232   unformat_free (&i);
233   return ret;
234 }