Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[mc] Define one struct per MC message type
[simgrid.git] / src / mc / mc_server.cpp
1 /* Copyright (c) 2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include <memory>
8 #include <system_error>
9
10 #include <poll.h>
11 #include <sys/types.h>
12 #include <sys/wait.h>
13 #include <sys/socket.h>
14 #include <sys/signalfd.h>
15
16 #include <xbt/log.h>
17
18 #include "mc_model_checker.h"
19 #include "mc_protocol.h"
20 #include "mc_server.h"
21 #include "mc_private.h"
22
23 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_server, mc, "MC server logic");
24
25 // HArdcoded index for now:
26 #define SOCKET_FD_INDEX 0
27 #define SIGNAL_FD_INDEX 1
28
29 mc_server_t mc_server;
30
31 s_mc_server::s_mc_server(pid_t pid, int socket)
32 {
33   this->pid = pid;
34   this->socket = socket;
35 }
36
37 void s_mc_server::start()
38 {
39   /* Wait for the target process to initialize and exchange a HELLO messages
40    * before trying to look at its memory map.
41    */
42   XBT_DEBUG("Greeting the MC client");
43   int res = MC_protocol_hello(socket);
44   if (res != 0)
45     throw std::system_error(res, std::system_category());
46   XBT_DEBUG("Greeted the MC client");
47
48   // Block SIGCHLD (this will be handled with accept/signalfd):
49   sigset_t set;
50   sigemptyset(&set);
51   sigaddset(&set, SIGCHLD);
52   if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
53     throw std::system_error(errno, std::system_category());
54
55   sigset_t full_set;
56   sigfillset(&full_set);
57
58   // Prepare data for poll:
59
60   struct pollfd* socket_pollfd = &fds[SOCKET_FD_INDEX];
61   socket_pollfd->fd = socket;
62   socket_pollfd->events = POLLIN;
63   socket_pollfd->revents = 0;
64
65   int signal_fd = signalfd(-1, &set, 0);
66   if (signal_fd == -1)
67     throw std::system_error(errno, std::system_category());
68
69   struct pollfd* signalfd_pollfd = &fds[SIGNAL_FD_INDEX];
70   signalfd_pollfd->fd = signal_fd;
71   signalfd_pollfd->events = POLLIN;
72   signalfd_pollfd->revents = 0;
73 }
74
75 void s_mc_server::shutdown()
76 {
77   XBT_DEBUG("Shuting down model-checker");
78
79   mc_process_t process = &mc_model_checker->process;
80   int status = process->status;
81   if (process->running) {
82     XBT_DEBUG("Killing process");
83     kill(process->pid, SIGTERM);
84     if (waitpid(process->pid, &status, 0) == -1)
85       throw std::system_error(errno, std::system_category());
86     // TODO, handle the case when the process does not want to die with a timeout
87     process->status = status;
88   }
89 }
90
91 void s_mc_server::exit()
92 {
93   // Finished:
94   int status = mc_model_checker->process.status;
95   if (WIFEXITED(status))
96     ::exit(WEXITSTATUS(status));
97   else if (WIFSIGNALED(status)) {
98     // Try to uplicate the signal of the model-checked process.
99     // This is a temporary hack so we don't try too hard.
100     kill(mc_model_checker->process.pid, WTERMSIG(status));
101     abort();
102   } else {
103     xbt_die("Unexpected status from model-checked process");
104   }
105 }
106
107 void s_mc_server::resume(mc_process_t process)
108 {
109   int socket = process->socket;
110   int res = MC_protocol_send_simple_message(socket, MC_MESSAGE_CONTINUE);
111   if (res)
112     throw std::system_error(res, std::system_category());
113 }
114
115 static
116 void throw_socket_error(int fd)
117 {
118   int error = 0;
119   socklen_t errlen = sizeof(error);
120   if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen) == -1)
121     error = errno;
122   throw std::system_error(error, std::system_category());
123 }
124
125 void s_mc_server::handle_events()
126 {
127   s_mc_message_t message;
128   struct pollfd* socket_pollfd = &fds[SOCKET_FD_INDEX];
129   struct pollfd* signalfd_pollfd = &fds[SIGNAL_FD_INDEX];
130
131   while(poll(fds, 2, -1) == -1) {
132     switch(errno) {
133     case EINTR:
134       continue;
135     default:
136       throw std::system_error(errno, std::system_category());
137     }
138   }
139
140   if (socket_pollfd->revents) {
141     if (socket_pollfd->revents & POLLIN) {
142       ssize_t size = recv(socket_pollfd->fd, &message, sizeof(message), MSG_DONTWAIT);
143       if (size == -1 && errno != EAGAIN)
144         throw std::system_error(errno, std::system_category());
145       else switch(message.type) {
146       case MC_MESSAGE_IGNORE_REGION:
147         XBT_DEBUG("Received ignored region");
148         // Ignored for now
149         break;
150       default:
151         xbt_die("Unexpected message from model-checked application");
152       }
153       return;
154     }
155     if (socket_pollfd->revents & POLLERR) {
156       throw_socket_error(socket_pollfd->fd);
157     }
158     if (socket_pollfd->revents & POLLHUP)
159       xbt_die("Socket hang up?");
160   }
161
162   if (signalfd_pollfd->revents) {
163     if (signalfd_pollfd->revents & POLLIN) {
164       this->handle_signals();
165       return;
166     }
167     if (signalfd_pollfd->revents & POLLERR) {
168       throw_socket_error(signalfd_pollfd->fd);
169     }
170     if (signalfd_pollfd->revents & POLLHUP)
171       xbt_die("Signalfd hang up?");
172   }
173 }
174
175 void s_mc_server::loop()
176 {
177   while (mc_model_checker->process.running)
178     this->handle_events();
179 }
180
181 void s_mc_server::handle_signals()
182 {
183   struct signalfd_siginfo info;
184   struct pollfd* signalfd_pollfd = &fds[SIGNAL_FD_INDEX];
185   while (1) {
186     ssize_t size = read(signalfd_pollfd->fd, &info, sizeof(info));
187     if (size == -1) {
188       if (errno == EINTR)
189         continue;
190       else
191         throw std::system_error(errno, std::system_category());
192     } else if (size != sizeof(info))
193         return throw std::runtime_error(
194           "Bad communication with model-checked application");
195     else
196       break;
197   }
198   this->on_signal(&info);
199 }
200
201 void s_mc_server::handle_waitpid()
202 {
203   XBT_DEBUG("Check for wait event");
204   int status;
205   pid_t pid;
206   while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
207     if (pid == -1) {
208       if (errno == ECHILD) {
209         // No more children:
210         if (mc_model_checker->process.running)
211           xbt_die("Inconsistent state");
212         else
213           break;
214       } else {
215         XBT_ERROR("Could not wait for pid: %s", strerror(errno));
216         throw std::system_error(errno, std::system_category());
217       }
218     }
219
220     if (pid == mc_model_checker->process.pid) {
221       if (WIFEXITED(status) || WIFSIGNALED(status)) {
222         XBT_DEBUG("Child process is over");
223         mc_model_checker->process.status = status;
224         mc_model_checker->process.running = false;
225       }
226     }
227   }
228 }
229
230 void s_mc_server::on_signal(const struct signalfd_siginfo* info)
231 {
232   switch(info->ssi_signo) {
233   case SIGCHLD:
234     this->handle_waitpid();
235     break;
236   default:
237     break;
238   }
239 }