blob: dcbcec7a1fe16982b3557b7e07849244fdb77970 [file] [log] [blame]
Brian Silvermand169fcd2013-02-27 13:18:47 -08001#include <stdio.h>
2#include <stdlib.h>
3#include <sys/types.h>
4#include <fcntl.h>
5#include <sys/inotify.h>
6#include <sys/stat.h>
7#include <sys/ioctl.h>
8#include <assert.h>
9#include <signal.h>
10#include <stdint.h>
11#include <errno.h>
12#include <string.h>
13#include <sys/wait.h>
14
15#include <map>
16#include <functional>
17#include <deque>
18#include <fstream>
19#include <queue>
20#include <list>
21#include <string>
22#include <vector>
23#include <memory>
24
25#include <event2/event.h>
26
27#include "aos/common/logging/logging.h"
28#include "aos/common/logging/logging_impl.h"
29#include "aos/atom_code/init.h"
30#include "aos/common/unique_malloc_ptr.h"
31#include "aos/common/time.h"
32
33// This is the main piece of code that starts all of the rest of the code and
34// restarts it when the binaries are modified.
35//
36// NOTE: This executable should never exit nicely. It catches all nice attempts
37// to exit, forwards them to all of the children that it has started, waits for
38// them to exit nicely, and then SIGKILLs anybody left (which will always
39// include itself).
40
41using ::std::unique_ptr;
42
43namespace aos {
44namespace starter {
45
46const char *child_list_file;
47
48pid_t group_leader;
49
50class EventBaseDeleter {
51 public:
52 void operator()(event_base *base) {
53 if (base == NULL) return;
54 event_base_free(base);
55 }
56};
57typedef unique_ptr<event_base, EventBaseDeleter> EventBaseUniquePtr;
58EventBaseUniquePtr libevent_base(NULL);
59
60class EventDeleter {
61 public:
62 void operator()(event *evt) {
63 if (evt == NULL) return;
64 if (event_del(evt) != 0) {
65 LOG(WARNING, "event_del(%p) failed\n", evt);
66 }
67 }
68};
69typedef unique_ptr<event, EventDeleter> EventUniquePtr;
70
71// Watches a file path for modifications. Will clean up when destroyed.
72class FileWatch {
73 public:
74 // Will call callback(value) when filename is modified.
75 // If value is NULL, then a pointer to this object will be passed instead.
76 FileWatch(std::string filename,
77 std::function<void(void *)> callback, void *value,
78 bool create = false, std::string check_filename = "")
79 : filename_(filename), callback_(callback), value_(value),
80 check_filename_(check_filename) {
81 watch_ = inotify_add_watch(notify_fd, filename.c_str(),
82 create ? IN_CREATE : (IN_ATTRIB | IN_MODIFY));
83 if (watch_ == -1) {
84 LOG(FATAL, "inotify_add_watch(%d, %s, IN_ATTRIB | IN_MODIFY)"
85 " failed with %d: %s\n",
86 notify_fd, filename.c_str(), errno, strerror(errno));
87 }
88 watchers[watch_] = this;
89 }
90 // Cleans up everything.
91 ~FileWatch() {
92 if (watch_ != -1) {
93 RemoveWatch();
94 }
95 }
96
97 // After calling this method, this object won't really be doing much of
98 // anything.
99 void RemoveWatch() {
100 assert(watch_ != -1);
101 if (inotify_rm_watch(notify_fd, watch_) == -1) {
102 LOG(WARNING, "inotify_rm_watch(%d, %d) failed with %d: %s\n",
103 notify_fd, watch_, errno, strerror(errno));
104 }
105 if (watchers[watch_] != this) {
106 LOG(WARNING, "watcher for %s didn't find itself in the map\n",
107 filename_.c_str());
108 } else {
109 watchers.erase(watch_);
110 }
111 watch_ = -1;
112 }
113
114 // This gets called whenever the watch for this file triggers.
115 void FileNotified(const char *filename) {
116 if (!check_filename_.empty()) {
117 if (filename == NULL) {
118 return;
119 }
120 if (std::string(filename) != check_filename_) {
121 return;
122 }
123 }
124
125 callback_((value_ == NULL) ? this : value_);
126 }
127
128 // This gets set up as the callback for EV_READ on the inotify file
129 // descriptor.
130 static void INotifyReadable(int /*fd*/, short /*events*/, void *) {
131 unsigned int to_read;
132 if (ioctl(notify_fd, FIONREAD, &to_read) < 0) {
133 LOG(FATAL, "FIONREAD(%d, %p) failed with %d: %s\n",
134 notify_fd, &to_read, errno, strerror(errno));
135 }
136 inotify_event *notifyevt = static_cast<inotify_event *>(malloc(to_read));
137 const char *end = reinterpret_cast<char *>(notifyevt) + to_read;
138 aos::unique_c_ptr<inotify_event> freer(notifyevt);
139
140 ssize_t ret = read(notify_fd, notifyevt, to_read);
141 if (ret < 0) {
142 LOG(FATAL, "read(%d, %p, %u) failed with %d: %s\n",
143 notify_fd, notifyevt, to_read, errno, strerror(errno));
144 }
145 if (static_cast<size_t>(ret) != to_read) {
146 LOG(ERROR, "read(%d, %p, %u) returned %zd instead of %u\n",
147 notify_fd, notifyevt, to_read, ret, to_read);
148 return;
149 }
150
151 // Might get multiple events.
152 while (true) {
153 if (watchers.count(notifyevt->wd) != 1) {
154 LOG(ERROR, "couldn't find whose watch ID %d is\n", notifyevt->wd);
155 return;
156 }
157 watchers[notifyevt->wd]->FileNotified((notifyevt->len > 0) ?
158 notifyevt->name : NULL);
159
160 notifyevt = reinterpret_cast<inotify_event *>(
161 reinterpret_cast<char *>(notifyevt) +
162 sizeof(*notifyevt) + notifyevt->len);
163 if (reinterpret_cast<char *>(notifyevt) >= end) break;
164 }
165 }
166
167 static void Init() {
168 notify_fd = inotify_init1(IN_CLOEXEC);
169 EventUniquePtr notify_event(event_new(libevent_base.get(), notify_fd,
170 EV_READ | EV_PERSIST,
171 FileWatch::INotifyReadable, NULL));
172 event_add(notify_event.release(), NULL);
173 }
174
175 private:
176 const std::string filename_;
177 const std::function<void(void *)> callback_;
178 void *const value_;
179 std::string check_filename_;
180
181 // The watch descriptor or -1 if we don't have one any more.
182 int watch_;
183
184 static std::map<int, FileWatch *> watchers;
185 static int notify_fd;
186};
187std::map<int, FileWatch *> FileWatch::watchers;
188int FileWatch::notify_fd;
189
190std::string RunCommand(std::string command) {
191 errno = 0;
192 FILE *which = popen(command.c_str(), "r");
193 if (which == NULL) {
194 LOG(FATAL, "popen(\"%s\", \"r\") failed with %d: %s\n",
195 command.c_str(), errno, strerror(errno));
196 }
197
198 size_t result_size = 128, read = 0;
199 unique_c_ptr<char> result(static_cast<char *>(malloc(result_size)));
200 while (true) {
201 if (read == result_size) {
202 result_size *= 2;
203 void *new_result = realloc(result.get(), result_size);
204 if (new_result == NULL) {
205 LOG(FATAL, "realloc(..., %zd) failed because of %d: %s\n",
206 result_size, errno, strerror(errno));
207 } else {
208 result.release();
209 result = unique_c_ptr<char>(static_cast<char *>(new_result));
210 }
211 }
212
213 size_t ret = fread(result.get() + read, 1, result_size - read, which);
214 if (ret < result_size - read) {
215 if (ferror(which)) {
216 LOG(FATAL, "couldn't finish reading output of \"%s\"\n",
217 command.c_str());
218 }
219 }
220 read += ret;
221 if (read > 0 && result.get()[read - 1] == '\n') {
222 break;
223 }
224
225 if (feof(which)) {
226 LOG(FATAL, "`%s` failed. didn't print anything\n", command.c_str());
227 }
228 }
229
230 // Get rid of the \n and anything after it.
231 *strchrnul(result.get(), '\n') = '\0';
232
233 int child_status = pclose(which);
234 if (child_status == -1) {
235 LOG(FATAL, "pclose(%p) failed with %d: %s\n", which,
236 errno, strerror(errno));
237 }
238
239 if (child_status != 0) {
240 LOG(FATAL, "`%s` failed. return %d\n", command.c_str(), child_status);
241 }
242
243 return std::string(result.get());
244}
245
246// Will call callback(arg) after time.
247void Timeout(time::Time time, void (*callback)(int, short, void *), void *arg) {
248 EventUniquePtr timeout(evtimer_new(libevent_base.get(), callback, arg));
249 struct timeval time_timeval = time.ToTimeval();
250 evtimer_add(timeout.release(), &time_timeval);
251}
252
253// Represents a child process. It will take care of restarting itself etc.
254class Child {
255 public:
256 // command is the (space-separated) command to run and its arguments
257 Child(const std::string &command) : pid_(-1), watcher_(NULL),
258 restart_timeout_(
259 evtimer_new(libevent_base.get(), StaticDoRestart, this)) {
260 const char *start, *end;
261 start = command.c_str();
262 while (true) {
263 end = strchrnul(start, ' ');
264 args_.push_back(std::string(start, end - start));
265 start = end + 1;
266 if (*end == '\0') {
267 break;
268 }
269 }
270
271 std::string full_binary = RunCommand("which " + args_[0]);
272
273 binary_ = full_binary + ".stm";
274
275 if (unlink(binary_.c_str()) != 0 && errno != ENOENT) {
276 LOG(FATAL, "removing %s failed because of %d: %s\n",
277 binary_.c_str(), errno, strerror(errno));
278 }
279 if (link(full_binary.c_str(), binary_.c_str()) != 0) {
280 LOG(FATAL, "link('%s', '%s') failed because of %d: %s\n",
281 full_binary.c_str(), binary_.c_str(), errno, strerror(errno));
282 }
283
284 watcher_ = unique_ptr<FileWatch>(
285 new FileWatch(full_binary, StaticFileModified, this));
286
287 Start();
288 }
289
290 pid_t pid() { return pid_; }
291
292 // This gets called whenever the actual process dies and should (probably) be
293 // restarted.
294 void ProcessDied() {
295 pid_ = -1;
296 restarts_.push(time::Time::Now());
297 if (restarts_.size() > kMaxRestartsNumber) {
298 time::Time oldest = restarts_.front();
299 restarts_.pop();
300 if ((time::Time::Now() - oldest) > kMaxRestartsTime) {
301 LOG(WARNING, "process %s getting restarted too often\n", name());
302 Timeout(kResumeWait, StaticStart, this);
303 return;
304 }
305 }
306 Start();
307 }
308
309 // Returns a name for logging purposes.
310 const char *name() {
311 return args_[0].c_str();
312 }
313
314 private:
315 struct CheckDiedStatus {
316 Child *self;
317 pid_t old_pid;
318 };
319
320 // How long to wait for a child to die nicely.
321 static const time::Time kProcessDieTime;
322
323 // How long to wait after the file is modified to restart it.
324 // This is important because some programs like modifying the binaries by
325 // writing them in little bits, which results in attempting to start partial
326 // binaries without this.
327 static const time::Time kRestartWaitTime;
328
329 // It will limit restarting to kMaxRestartsNumber every kMaxRestartsTime.
330 static const time::Time kMaxRestartsTime;
331 static const size_t kMaxRestartsNumber = 5;
332 // How long to wait if it gets restarted too many times.
333 static const time::Time kResumeWait;
334 // A history of the times that this process has been restarted.
335 std::queue<time::Time, std::list<time::Time>> restarts_;
336
337 pid_t pid_;
338
339 std::deque<std::string> args_;
340 std::string binary_;
341
342 unique_ptr<FileWatch> watcher_;
343
344 // An event that restarts after kRestartWaitTime.
345 EventUniquePtr restart_timeout_;
346
347 static void StaticFileModified(void *self) {
348 static_cast<Child *>(self)->FileModified();
349 }
350 void FileModified() {
351 struct timeval restart_time_timeval = kRestartWaitTime.ToTimeval();
352 // This will reset the timeout again if it hasn't run yet.
353 evtimer_add(restart_timeout_.get(), &restart_time_timeval);
354 }
355
356 static void StaticDoRestart(int, short, void *self) {
357 static_cast<Child *>(self)->DoRestart();
358 }
359
360 void DoRestart() {
361 if (pid_ != -1) {
362 LOG(DEBUG, "sending SIGTERM to child %d to restart it\n", pid_);
363 if (kill(pid_, SIGTERM) == -1) {
364 LOG(WARNING, "kill(%d, SIGTERM) failed with %d: %s\n",
365 pid_, errno, strerror(errno));
366 }
367 CheckDiedStatus *status = new CheckDiedStatus();
368 status->self = this;
369 status->old_pid = pid_;
370 Timeout(kProcessDieTime, StaticCheckDied, status);
371 }
372 }
373
374 static void StaticCheckDied(int, short, void *status_in) {
375 CheckDiedStatus *status = static_cast<CheckDiedStatus *>(status_in);
376 status->self->CheckDied(status->old_pid);
377 delete status;
378 }
379 void CheckDied(pid_t old_pid) {
380 if (pid_ == old_pid) {
381 LOG(WARNING, "child %d refused to die\n", old_pid);
382 if (kill(old_pid, SIGKILL) == -1) {
383 LOG(WARNING, "kill(%d, SIGKILL) failed with %d: %s\n",
384 old_pid, errno, strerror(errno));
385 }
386 }
387 }
388
389 static void StaticStart(int, short, void *self) {
390 static_cast<Child *>(self)->Start();
391 }
392 void Start() {
393 if (pid_ != -1) {
394 LOG(WARNING, "calling Start() but already have child %d running\n",
395 pid_);
396 kill(pid_, SIGKILL);
397 pid_ = -1;
398 }
399 if ((pid_ = fork()) == 0) {
400 ssize_t args_size = args_.size();
401 const char **argv = new const char *[args_size + 1];
402 for (int i = 0; i < args_size; ++i) {
403 argv[i] = args_[i].c_str();
404 }
405 argv[args_size] = NULL;
406 // The const_cast is safe because no code that might care if it gets
407 // modified can run afterwards.
408 execv(binary_.c_str(), const_cast<char **>(argv));
409 LOG(FATAL, "execv(%s, %p) failed with %d: %s\n",
410 binary_.c_str(), argv, errno, strerror(errno));
411 _exit(EXIT_FAILURE);
412 }
413 if (pid_ == -1) {
414 LOG(WARNING, "forking to \"%s\" failed with %d: %s\n",
415 binary_.c_str(), errno, strerror(errno));
416 }
417 }
418};
419const time::Time Child::kProcessDieTime = time::Time::InSeconds(0.5);
420const time::Time Child::kMaxRestartsTime = time::Time::InSeconds(2);
421const time::Time Child::kResumeWait = time::Time::InSeconds(1.5);
422const time::Time Child::kRestartWaitTime = time::Time::InSeconds(1.5);
423
424// This is where all of the Child instances except core live.
425std::vector<unique_ptr<Child>> children;
426// A global palce to hold on to which child is core.
427unique_ptr<Child> core(NULL);
428
429void kill_children(bool try_nice) {
430 if (try_nice) {
431 static const int kNiceStopSignal = SIGTERM;
432 static const time::Time kNiceWaitTime = time::Time::InSeconds(1);
433
434 // Make sure that we don't just nicely stop ourself...
435 sigset_t mask;
436 sigemptyset(&mask);
437 sigaddset(&mask, kNiceStopSignal);
438 sigprocmask(SIG_BLOCK, &mask, NULL);
439
440 kill(-group_leader, kNiceStopSignal);
441 time::SleepFor(kNiceWaitTime);
442 }
443 // Send SIGKILL to our whole process group, which will forcibly terminate any
444 // of them that are still running (us for sure, maybe more too).
445 kill(-group_leader, SIGKILL);
446}
447
448void exit_handler() {
449 kill_children(true);
450}
451void signal_pg_kill_handler(int signum) {
452 // If we get SIGSEGV or something who knows what's happening and we should
453 // just kill everybody immediately.
454 kill_children(signum == SIGHUP || signum == SIGINT || signum == SIGQUIT ||
455 signum == SIGABRT || signum == SIGPIPE || signum == SIGTERM ||
456 signum == SIGXCPU);
457}
458
459const unique_ptr<Child> &FindChild(pid_t pid) {
460 for (auto it = children.begin(); it != children.end(); ++it) {
461 if (pid == (*it)->pid()) {
462 return *it;
463 }
464 }
465
466 if (pid == core->pid()) {
467 return core;
468 }
469
470 static const unique_ptr<Child> kNothing(NULL);
471 return kNothing;
472}
473
474void sigchld_received(int /*fd*/, short events, void *) {
475 if (events != EV_SIGNAL) {
476 LOG(WARNING, "received an event that wasn't EV_SIGNAL\n");
477 return;
478 }
479
480 // In a while loop in case we miss any SIGCHLDs.
481 while (true) {
482 siginfo_t infop;
483 infop.si_pid = 0;
484 if (waitid(P_ALL, 0, &infop, WEXITED | WSTOPPED | WNOHANG) != 0) {
485 LOG(WARNING, "waitid failed with %d: %s", errno, strerror(errno));
486 }
487 if (infop.si_pid == 0) {
488 return; // no more child process changes pending
489 }
490
491 pid_t pid = infop.si_pid;
492 int status = infop.si_status;
493 const unique_ptr<Child> &child = FindChild(pid);
494 if (child) {
495 switch (infop.si_code) {
496 case CLD_EXITED:
497 LOG(WARNING, "child %d (%s) exited with status %d\n",
498 pid, child->name(), status);
499 break;
500 case CLD_DUMPED:
501 LOG(INFO, "child %d actually dumped core. "
502 "falling through to killed by signal case\n", pid);
503 case CLD_KILLED:
504 // If somebody (possibly us) sent it SIGTERM that means that they just
505 // want it to stop, so it stopping isn't a WARNING.
506 LOG((status == SIGTERM) ? DEBUG : WARNING,
507 "child %d (%s) was killed by signal %d (%s)\n",
508 pid, child->name(), status,
509 strsignal(status));
510 break;
511 case CLD_STOPPED:
512 LOG(WARNING, "child %d (%s) was stopped by signal %d "
513 "(giving it a SIGCONT(%d))\n",
514 pid, child->name(), status, SIGCONT);
515 kill(pid, SIGCONT);
516 continue;
517 default:
518 LOG(WARNING, "something happened to child %d (%s) (killing it)\n",
519 pid, child->name());
520 kill(pid, SIGKILL);
521 continue;
522 }
523 } else {
524 LOG(WARNING, "couldn't find child for pid %d\n", pid);
525 }
526 if (child == core) {
527 fprintf(stderr, "starter: si_code=%d CLD_EXITED=%d CLD_DUMPED=%d "
528 "CLD_KILLED=%d CLD_STOPPED=%d si_status=%d (sig '%s')\n",
529 infop.si_code, CLD_EXITED, CLD_DUMPED, CLD_KILLED,
530 CLD_STOPPED, status, strsignal(status));
531 LOG(FATAL, "core died\n");
532 }
533
534 child->ProcessDied();
535 }
536}
537
538// This is the callback for when core creates the file indicating that it has
539// started.
540void Run(void *watch) {
541 // Make it so it doesn't keep on seeing random changes in /tmp.
542 static_cast<FileWatch *>(watch)->RemoveWatch();
543
544 // It's safe now because core is up.
545 aos::InitNRT();
546
547 std::ifstream list_file(child_list_file);
548
549 // while it's not at EOF
550 while (true) {
551 std::string child_name;
552 getline(list_file, child_name);
553 if ((list_file.rdstate() & std::ios_base::eofbit) != 0) {
554 break;
555 }
556 if (list_file.rdstate() != 0) {
557 LOG(FATAL, "reading input file %s failed\n", child_list_file);
558 }
559 children.push_back(unique_ptr<Child>(new Child(child_name)));
560 }
561
562 EventUniquePtr sigchld(event_new(libevent_base.get(), SIGCHLD,
563 EV_SIGNAL | EV_PERSIST,
564 sigchld_received, NULL));
565 event_add(sigchld.release(), NULL);
566}
567
568void Main() {
569 logging::Init();
570 // TODO(brians) tell logging that using the root logger from here until we
571 // bring up shm is ok
572
573 group_leader = setpgrp();
574
575 // Make sure that we kill all children when we exit.
576 atexit(exit_handler);
577 // Do it on some signals too (ones that we otherwise tend to receive and then
578 // leave all of our children going).
579 signal(SIGHUP, signal_pg_kill_handler);
580 signal(SIGINT, signal_pg_kill_handler);
581 signal(SIGQUIT, signal_pg_kill_handler);
582 signal(SIGILL, signal_pg_kill_handler);
583 signal(SIGABRT, signal_pg_kill_handler);
584 signal(SIGFPE, signal_pg_kill_handler);
585 signal(SIGSEGV, signal_pg_kill_handler);
586 signal(SIGPIPE, signal_pg_kill_handler);
587 signal(SIGTERM, signal_pg_kill_handler);
588 signal(SIGBUS, signal_pg_kill_handler);
589 signal(SIGXCPU, signal_pg_kill_handler);
590
591 libevent_base = EventBaseUniquePtr(event_base_new());
592
593 FileWatch::Init();
594
595 static const std::string kCoreTouchFileDir = "/tmp/";
596 std::string core_touch_file = "starter.";
597 core_touch_file += std::to_string(static_cast<intmax_t>(getpid()));
598 core_touch_file += ".core_touch_file";
599 FileWatch core_touch_file_watch(kCoreTouchFileDir, Run, NULL, true,
600 core_touch_file);
601 core = unique_ptr<Child>(
602 new Child("core " + kCoreTouchFileDir + core_touch_file));
603
604 FILE *pid_file = fopen("/tmp/starter.pid", "w");
605 if (pid_file == NULL) {
606 LOG(FATAL, "fopen(/tmp/starter.pid, w) failed with %d: %s\n",
607 errno, strerror(errno));
608 } else {
609 if (fprintf(pid_file, "%d", core->pid()) == -1) {
610 fprintf(stderr, "starter: error: fprintf(pid_file, core(=%d)) failed "
611 "with %d: %s",
612 core->pid(), errno, strerror(errno));
613 }
614 fclose(pid_file);
615 }
616
617 LOG(INFO, "waiting for %s to appear\n", core_touch_file.c_str());
618
619 event_base_dispatch(libevent_base.get());
620 LOG(FATAL, "event_base_dispatch(%p) returned\n", libevent_base.get());
621}
622
623} // namespace starter
624} // namespace aos
625
626int main(int argc, char *argv[]) {
627 if (argc < 2) {
628 fputs("starter: error: need an argument specifying what file to use\n",
629 stderr);
630 exit(EXIT_FAILURE);
631 } else if(argc > 2) {
632 fputs("starter: warning: too many arguments\n", stderr);
633 }
634 aos::starter::child_list_file = argv[1];
635
636 aos::starter::Main();
637}