mcproc 2016.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. checksums.yaml +7 -0
  2. data/Announce.txt +135 -0
  3. data/Gemfile +9 -0
  4. data/History.txt +469 -0
  5. data/LICENSE +22 -0
  6. data/README.md +37 -0
  7. data/Rakefile +185 -0
  8. data/TODO.md +37 -0
  9. data/bin/mcproc +134 -0
  10. data/doc/intro.asciidoc +20 -0
  11. data/doc/mcproc.asciidoc +1592 -0
  12. data/ext/god/.gitignore +5 -0
  13. data/ext/god/extconf.rb +56 -0
  14. data/ext/god/kqueue_handler.c +133 -0
  15. data/ext/god/netlink_handler.c +182 -0
  16. data/lib/god.rb +780 -0
  17. data/lib/god/behavior.rb +52 -0
  18. data/lib/god/behaviors/clean_pid_file.rb +21 -0
  19. data/lib/god/behaviors/clean_unix_socket.rb +21 -0
  20. data/lib/god/behaviors/notify_when_flapping.rb +51 -0
  21. data/lib/god/cli/command.rb +268 -0
  22. data/lib/god/cli/run.rb +170 -0
  23. data/lib/god/cli/version.rb +23 -0
  24. data/lib/god/compat19.rb +33 -0
  25. data/lib/god/condition.rb +96 -0
  26. data/lib/god/conditions/always.rb +36 -0
  27. data/lib/god/conditions/complex.rb +86 -0
  28. data/lib/god/conditions/cpu_usage.rb +80 -0
  29. data/lib/god/conditions/degrading_lambda.rb +52 -0
  30. data/lib/god/conditions/disk_usage.rb +32 -0
  31. data/lib/god/conditions/file_mtime.rb +28 -0
  32. data/lib/god/conditions/file_touched.rb +44 -0
  33. data/lib/god/conditions/flapping.rb +128 -0
  34. data/lib/god/conditions/http_response_code.rb +184 -0
  35. data/lib/god/conditions/lambda.rb +25 -0
  36. data/lib/god/conditions/memory_usage.rb +82 -0
  37. data/lib/god/conditions/process_exits.rb +66 -0
  38. data/lib/god/conditions/process_running.rb +63 -0
  39. data/lib/god/conditions/socket_responding.rb +142 -0
  40. data/lib/god/conditions/tries.rb +44 -0
  41. data/lib/god/configurable.rb +57 -0
  42. data/lib/god/contact.rb +114 -0
  43. data/lib/god/contacts/airbrake.rb +44 -0
  44. data/lib/god/contacts/campfire.rb +121 -0
  45. data/lib/god/contacts/email.rb +130 -0
  46. data/lib/god/contacts/hipchat.rb +117 -0
  47. data/lib/god/contacts/jabber.rb +75 -0
  48. data/lib/god/contacts/prowl.rb +57 -0
  49. data/lib/god/contacts/scout.rb +55 -0
  50. data/lib/god/contacts/sensu.rb +59 -0
  51. data/lib/god/contacts/slack.rb +98 -0
  52. data/lib/god/contacts/statsd.rb +46 -0
  53. data/lib/god/contacts/twitter.rb +51 -0
  54. data/lib/god/contacts/webhook.rb +74 -0
  55. data/lib/god/driver.rb +238 -0
  56. data/lib/god/errors.rb +24 -0
  57. data/lib/god/event_handler.rb +112 -0
  58. data/lib/god/event_handlers/dummy_handler.rb +13 -0
  59. data/lib/god/event_handlers/kqueue_handler.rb +17 -0
  60. data/lib/god/event_handlers/netlink_handler.rb +13 -0
  61. data/lib/god/logger.rb +109 -0
  62. data/lib/god/metric.rb +87 -0
  63. data/lib/god/process.rb +381 -0
  64. data/lib/god/registry.rb +32 -0
  65. data/lib/god/simple_logger.rb +59 -0
  66. data/lib/god/socket.rb +113 -0
  67. data/lib/god/sugar.rb +62 -0
  68. data/lib/god/sys_logger.rb +45 -0
  69. data/lib/god/system/portable_poller.rb +42 -0
  70. data/lib/god/system/process.rb +50 -0
  71. data/lib/god/system/slash_proc_poller.rb +92 -0
  72. data/lib/god/task.rb +552 -0
  73. data/lib/god/timeline.rb +25 -0
  74. data/lib/god/trigger.rb +43 -0
  75. data/lib/god/watch.rb +340 -0
  76. data/mcproc.gemspec +192 -0
  77. data/test/configs/child_events/child_events.god +44 -0
  78. data/test/configs/child_events/simple_server.rb +3 -0
  79. data/test/configs/child_polls/child_polls.god +37 -0
  80. data/test/configs/child_polls/simple_server.rb +12 -0
  81. data/test/configs/complex/complex.god +59 -0
  82. data/test/configs/complex/simple_server.rb +3 -0
  83. data/test/configs/contact/contact.god +118 -0
  84. data/test/configs/contact/simple_server.rb +3 -0
  85. data/test/configs/daemon_events/daemon_events.god +37 -0
  86. data/test/configs/daemon_events/simple_server.rb +8 -0
  87. data/test/configs/daemon_events/simple_server_stop.rb +11 -0
  88. data/test/configs/daemon_polls/daemon_polls.god +17 -0
  89. data/test/configs/daemon_polls/simple_server.rb +6 -0
  90. data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
  91. data/test/configs/degrading_lambda/tcp_server.rb +15 -0
  92. data/test/configs/keepalive/keepalive.god +9 -0
  93. data/test/configs/keepalive/keepalive.rb +12 -0
  94. data/test/configs/lifecycle/lifecycle.god +25 -0
  95. data/test/configs/matias/matias.god +50 -0
  96. data/test/configs/real.rb +59 -0
  97. data/test/configs/running_load/running_load.god +16 -0
  98. data/test/configs/stop_options/simple_server.rb +12 -0
  99. data/test/configs/stop_options/stop_options.god +39 -0
  100. data/test/configs/stress/simple_server.rb +3 -0
  101. data/test/configs/stress/stress.god +15 -0
  102. data/test/configs/task/logs/.placeholder +0 -0
  103. data/test/configs/task/task.god +26 -0
  104. data/test/configs/test.rb +61 -0
  105. data/test/configs/usr1_trapper.rb +10 -0
  106. data/test/helper.rb +172 -0
  107. data/test/suite.rb +6 -0
  108. data/test/test_airbrake.rb +14 -0
  109. data/test/test_behavior.rb +18 -0
  110. data/test/test_campfire.rb +22 -0
  111. data/test/test_condition.rb +52 -0
  112. data/test/test_conditions_disk_usage.rb +50 -0
  113. data/test/test_conditions_http_response_code.rb +109 -0
  114. data/test/test_conditions_process_running.rb +40 -0
  115. data/test/test_conditions_socket_responding.rb +176 -0
  116. data/test/test_conditions_tries.rb +67 -0
  117. data/test/test_contact.rb +109 -0
  118. data/test/test_driver.rb +26 -0
  119. data/test/test_email.rb +34 -0
  120. data/test/test_event_handler.rb +82 -0
  121. data/test/test_god.rb +710 -0
  122. data/test/test_god_system.rb +201 -0
  123. data/test/test_handlers_kqueue_handler.rb +16 -0
  124. data/test/test_hipchat.rb +23 -0
  125. data/test/test_jabber.rb +29 -0
  126. data/test/test_logger.rb +55 -0
  127. data/test/test_metric.rb +74 -0
  128. data/test/test_process.rb +263 -0
  129. data/test/test_prowl.rb +15 -0
  130. data/test/test_registry.rb +15 -0
  131. data/test/test_sensu.rb +11 -0
  132. data/test/test_slack.rb +57 -0
  133. data/test/test_socket.rb +34 -0
  134. data/test/test_statsd.rb +22 -0
  135. data/test/test_sugar.rb +42 -0
  136. data/test/test_system_portable_poller.rb +17 -0
  137. data/test/test_system_process.rb +30 -0
  138. data/test/test_task.rb +246 -0
  139. data/test/test_timeline.rb +37 -0
  140. data/test/test_trigger.rb +63 -0
  141. data/test/test_watch.rb +286 -0
  142. data/test/test_webhook.rb +22 -0
  143. metadata +475 -0
@@ -0,0 +1,5 @@
1
+ Makefile
2
+ kqueue_handler_ext.bundle
3
+ kqueue_handler.o
4
+ netlink_handler_ext.bundle
5
+ netlink_handler.o
@@ -0,0 +1,56 @@
1
+ require 'mkmf'
2
+
3
+ fail = false
4
+
5
+ def create_dummy_makefile
6
+ File.open("Makefile", 'w') do |f|
7
+ f.puts "all:"
8
+ f.puts "install:"
9
+ end
10
+ end
11
+
12
+ have_func('rb_wait_for_single_fd')
13
+ case RUBY_PLATFORM
14
+ when /bsd/i, /darwin/i
15
+ unless have_header('sys/event.h')
16
+ puts
17
+ puts "Missing 'sys/event.h' header"
18
+ fail = true
19
+ end
20
+
21
+ if fail
22
+ puts
23
+ puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
24
+ create_dummy_makefile
25
+ else
26
+ create_makefile 'kqueue_handler_ext'
27
+ end
28
+ when /linux/i
29
+ unless have_header('linux/netlink.h')
30
+ puts
31
+ puts "Missing 'linux/netlink.h' header(s)"
32
+ puts "You may need to install a header package for your system"
33
+ fail = true
34
+ end
35
+
36
+ unless have_header('linux/connector.h') && have_header('linux/cn_proc.h')
37
+ puts
38
+ puts "Missing 'linux/connector.h', or 'linux/cn_proc.h' header(s)"
39
+ puts "These are only available in Linux kernel 2.6.15 and later (run `uname -a` to find yours)"
40
+ puts "You may need to install a header package for your system"
41
+ fail = true
42
+ end
43
+
44
+ if fail
45
+ puts
46
+ puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
47
+ create_dummy_makefile
48
+ else
49
+ create_makefile 'netlink_handler_ext'
50
+ end
51
+ else
52
+ puts
53
+ puts "Unsupported platform '#{RUBY_PLATFORM}'. Supported platforms are BSD, DARWIN, and LINUX."
54
+ puts "Your god installation will not support event conditions."
55
+ create_dummy_makefile
56
+ end
@@ -0,0 +1,133 @@
1
+ #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
2
+
3
+ #include <ruby.h>
4
+ #include <sys/event.h>
5
+ #include <sys/time.h>
6
+ #include <errno.h>
7
+
8
+ #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
9
+ #include <ruby/io.h>
10
+ #endif
11
+
12
+ static VALUE mGod;
13
+ static VALUE cKQueueHandler;
14
+ static VALUE cEventHandler;
15
+
16
+ static ID proc_exit;
17
+ static ID proc_fork;
18
+ static ID m_call;
19
+ static ID m_size;
20
+ static ID m_deregister;
21
+
22
+ static int kq;
23
+ int num_events;
24
+
25
+ #define NUM_EVENTS FIX2INT(rb_funcall(rb_cv_get(cEventHandler, "@@actions"), m_size, 0))
26
+
27
+ VALUE
28
+ kqh_event_mask(VALUE klass, VALUE sym)
29
+ {
30
+ ID id = SYM2ID(sym);
31
+ if (proc_exit == id) {
32
+ return UINT2NUM(NOTE_EXIT);
33
+ } else if (proc_fork == id) {
34
+ return UINT2NUM(NOTE_FORK);
35
+ } else {
36
+ rb_raise(rb_eNotImpError, "Event `%s` not implemented", rb_id2name(id));
37
+ }
38
+
39
+ return Qnil;
40
+ }
41
+
42
+
43
+ VALUE
44
+ kqh_monitor_process(VALUE klass, VALUE pid, VALUE mask)
45
+ {
46
+ struct kevent new_event;
47
+ ID event;
48
+
49
+ (void)event; //!< Silence warning about unused var, should be removed?
50
+
51
+ u_int fflags = NUM2UINT(mask);
52
+
53
+ EV_SET(&new_event, FIX2UINT(pid), EVFILT_PROC,
54
+ EV_ADD | EV_ENABLE, fflags, 0, 0);
55
+
56
+ if (-1 == kevent(kq, &new_event, 1, NULL, 0, NULL)) {
57
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
58
+ }
59
+
60
+ num_events = NUM_EVENTS;
61
+
62
+ return Qnil;
63
+ }
64
+
65
+ VALUE
66
+ kqh_handle_events()
67
+ {
68
+ int nevents, i, num_to_fetch;
69
+ struct kevent *events;
70
+
71
+ #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
72
+ rb_wait_for_single_fd(kq, RB_WAITFD_IN, NULL);
73
+ #else
74
+ fd_set read_set;
75
+
76
+ FD_ZERO(&read_set);
77
+ FD_SET(kq, &read_set);
78
+
79
+ // Don't actually run this method until we've got an event
80
+ rb_thread_select(kq + 1, &read_set, NULL, NULL, NULL);
81
+ #endif
82
+ // Grabbing num_events once for thread safety
83
+ num_to_fetch = num_events;
84
+ events = (struct kevent*)malloc(num_to_fetch * sizeof(struct kevent));
85
+
86
+ if (NULL == events) {
87
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
88
+ }
89
+
90
+ nevents = kevent(kq, NULL, 0, events, num_to_fetch, NULL);
91
+
92
+ if (-1 == nevents) {
93
+ free(events);
94
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
95
+ } else {
96
+ for (i = 0; i < nevents; i++) {
97
+ if (events[i].fflags & NOTE_EXIT) {
98
+ rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_exit));
99
+ } else if (events[i].fflags & NOTE_FORK) {
100
+ rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_fork));
101
+ }
102
+ }
103
+ }
104
+
105
+ free(events);
106
+
107
+ return INT2FIX(nevents);
108
+ }
109
+
110
+ void
111
+ Init_kqueue_handler_ext()
112
+ {
113
+ kq = kqueue();
114
+
115
+ if (kq == -1) {
116
+ rb_raise(rb_eStandardError, "kqueue initilization failed");
117
+ }
118
+
119
+ proc_exit = rb_intern("proc_exit");
120
+ proc_fork = rb_intern("proc_fork");
121
+ m_call = rb_intern("call");
122
+ m_size = rb_intern("size");
123
+ m_deregister = rb_intern("deregister");
124
+
125
+ mGod = rb_const_get(rb_cObject, rb_intern("God"));
126
+ cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
127
+ cKQueueHandler = rb_define_class_under(mGod, "KQueueHandler", rb_cObject);
128
+ rb_define_singleton_method(cKQueueHandler, "monitor_process", kqh_monitor_process, 2);
129
+ rb_define_singleton_method(cKQueueHandler, "handle_events", kqh_handle_events, 0);
130
+ rb_define_singleton_method(cKQueueHandler, "event_mask", kqh_event_mask, 1);
131
+ }
132
+
133
+ #endif
@@ -0,0 +1,182 @@
1
+ #ifdef __linux__ /* only build on linux */
2
+
3
+ #include <ruby.h>
4
+
5
+ #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
6
+ #include <ruby/io.h>
7
+ #endif
8
+
9
+
10
+ #include <sys/types.h>
11
+ #include <unistd.h>
12
+ #include <sys/socket.h>
13
+ #include <linux/netlink.h>
14
+ #include <linux/connector.h>
15
+ #define _LINUX_TIME_H
16
+ #include <linux/cn_proc.h>
17
+ #include <errno.h>
18
+
19
+ static VALUE mGod;
20
+ static VALUE cNetlinkHandler;
21
+ static VALUE cEventHandler;
22
+
23
+ static ID proc_exit;
24
+ static ID proc_fork;
25
+ static ID m_call;
26
+ static ID m_watching_pid;
27
+
28
+ static int nl_sock; /* socket for netlink connection */
29
+
30
+
31
+ VALUE
32
+ nlh_handle_events()
33
+ {
34
+ char buff[CONNECTOR_MAX_MSG_SIZE];
35
+ struct nlmsghdr *hdr;
36
+ struct proc_event *event;
37
+
38
+ VALUE extra_data;
39
+
40
+ #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
41
+ int ret;
42
+ if((ret = rb_wait_for_single_fd(nl_sock, RB_WAITFD_IN, NULL)) < 0){
43
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
44
+ }
45
+ /* If there were no events detected, return */
46
+ if(!(ret & RB_WAITFD_IN)){
47
+ return INT2FIX(0);
48
+ }
49
+ #else
50
+ fd_set fds;
51
+
52
+ FD_ZERO(&fds);
53
+ FD_SET(nl_sock, &fds);
54
+
55
+ if (0 > rb_thread_select(nl_sock + 1, &fds, NULL, NULL, NULL)) {
56
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
57
+ }
58
+
59
+ /* If there were no events detected, return */
60
+ if (! FD_ISSET(nl_sock, &fds)) {
61
+ return INT2FIX(0);
62
+ }
63
+ #endif
64
+
65
+ /* if there are events, make calls */
66
+ if (-1 == recv(nl_sock, buff, sizeof(buff), 0)) {
67
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
68
+ }
69
+
70
+ hdr = (struct nlmsghdr *)buff;
71
+
72
+ if (NLMSG_ERROR == hdr->nlmsg_type) {
73
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
74
+ } else if (NLMSG_DONE == hdr->nlmsg_type) {
75
+
76
+ event = (struct proc_event *)((struct cn_msg *)NLMSG_DATA(hdr))->data;
77
+
78
+ switch(event->what) {
79
+ case PROC_EVENT_EXIT:
80
+ if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.exit.process_pid))) {
81
+ return INT2FIX(0);
82
+ }
83
+
84
+ extra_data = rb_hash_new();
85
+ rb_hash_aset(extra_data, ID2SYM(rb_intern("pid")), INT2FIX(event->event_data.exit.process_pid));
86
+ rb_hash_aset(extra_data, ID2SYM(rb_intern("exit_code")), INT2FIX(event->event_data.exit.exit_code));
87
+ rb_hash_aset(extra_data, ID2SYM(rb_intern("exit_signal")), INT2FIX(event->event_data.exit.exit_signal));
88
+ rb_hash_aset(extra_data, ID2SYM(rb_intern("thread_group_id")), INT2FIX(event->event_data.exit.process_tgid));
89
+
90
+ rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.exit.process_pid), ID2SYM(proc_exit), extra_data);
91
+ return INT2FIX(1);
92
+
93
+ case PROC_EVENT_FORK:
94
+ if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.fork.parent_pid))) {
95
+ return INT2FIX(0);
96
+ }
97
+
98
+ extra_data = rb_hash_new();
99
+ rb_hash_aset(extra_data, ID2SYM(rb_intern("parent_pid")), INT2FIX(event->event_data.fork.parent_pid));
100
+ rb_hash_aset(extra_data, ID2SYM(rb_intern("parent_thread_group_id")), INT2FIX(event->event_data.fork.parent_tgid));
101
+ rb_hash_aset(extra_data, ID2SYM(rb_intern("child_pid")), INT2FIX(event->event_data.fork.child_pid));
102
+ rb_hash_aset(extra_data, ID2SYM(rb_intern("child_thread_group_id")), INT2FIX(event->event_data.fork.child_tgid));
103
+
104
+ rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.fork.parent_pid), ID2SYM(proc_fork), extra_data);
105
+ return INT2FIX(1);
106
+
107
+ default:
108
+ break;
109
+ }
110
+ }
111
+
112
+ return Qnil;
113
+ }
114
+
115
+
116
+ #define NL_MESSAGE_SIZE (sizeof(struct nlmsghdr) + sizeof(struct cn_msg) + \
117
+ sizeof(int))
118
+
119
+ void
120
+ connect_to_netlink()
121
+ {
122
+ struct sockaddr_nl sa_nl; /* netlink interface info */
123
+ char buff[NL_MESSAGE_SIZE];
124
+ struct nlmsghdr *hdr; /* for telling netlink what we want */
125
+ struct cn_msg *msg; /* the actual connector message */
126
+
127
+ /* connect to netlink socket */
128
+ nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
129
+
130
+ if (-1 == nl_sock) {
131
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
132
+ }
133
+
134
+ bzero(&sa_nl, sizeof(sa_nl));
135
+ sa_nl.nl_family = AF_NETLINK;
136
+ sa_nl.nl_groups = CN_IDX_PROC;
137
+ sa_nl.nl_pid = getpid();
138
+
139
+ if (-1 == bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl))) {
140
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
141
+ }
142
+
143
+ /* Fill header */
144
+ hdr = (struct nlmsghdr *)buff;
145
+ hdr->nlmsg_len = NL_MESSAGE_SIZE;
146
+ hdr->nlmsg_type = NLMSG_DONE;
147
+ hdr->nlmsg_flags = 0;
148
+ hdr->nlmsg_seq = 0;
149
+ hdr->nlmsg_pid = getpid();
150
+
151
+ /* Fill message */
152
+ msg = (struct cn_msg *)NLMSG_DATA(hdr);
153
+ msg->id.idx = CN_IDX_PROC; /* Connecting to process information */
154
+ msg->id.val = CN_VAL_PROC;
155
+ msg->seq = 0;
156
+ msg->ack = 0;
157
+ msg->flags = 0;
158
+ msg->len = sizeof(int);
159
+ *(int*)msg->data = PROC_CN_MCAST_LISTEN;
160
+
161
+ if (-1 == send(nl_sock, hdr, hdr->nlmsg_len, 0)) {
162
+ rb_raise(rb_eStandardError, "%s", strerror(errno));
163
+ }
164
+ }
165
+
166
+ void
167
+ Init_netlink_handler_ext()
168
+ {
169
+ proc_exit = rb_intern("proc_exit");
170
+ proc_fork = rb_intern("proc_fork");
171
+ m_call = rb_intern("call");
172
+ m_watching_pid = rb_intern("watching_pid?");
173
+
174
+ mGod = rb_const_get(rb_cObject, rb_intern("God"));
175
+ cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
176
+ cNetlinkHandler = rb_define_class_under(mGod, "NetlinkHandler", rb_cObject);
177
+ rb_define_singleton_method(cNetlinkHandler, "handle_events", nlh_handle_events, 0);
178
+
179
+ connect_to_netlink();
180
+ }
181
+
182
+ #endif
data/lib/god.rb ADDED
@@ -0,0 +1,780 @@
1
+ # Bail out before loading anything unless this flag is set.
2
+ #
3
+ # We are doing this to guard against bundler autoloading because there is
4
+ # no value in loading god in most processes.
5
+ if $load_god
6
+
7
+ # core
8
+ require 'stringio'
9
+ require 'fileutils'
10
+
11
+ begin
12
+ require 'fastthread'
13
+ rescue LoadError
14
+ ensure
15
+ require 'thread'
16
+ end
17
+
18
+ # stdlib
19
+
20
+ # internal requires
21
+ require 'god/errors'
22
+ require 'god/simple_logger'
23
+ require 'god/logger'
24
+ require 'god/sugar'
25
+
26
+ require 'god/system/process'
27
+ require 'god/system/portable_poller'
28
+ require 'god/system/slash_proc_poller'
29
+
30
+ require 'god/timeline'
31
+ require 'god/configurable'
32
+
33
+ require 'god/task'
34
+
35
+ require 'god/behavior'
36
+ require 'god/behaviors/clean_pid_file'
37
+ require 'god/behaviors/clean_unix_socket'
38
+ require 'god/behaviors/notify_when_flapping'
39
+
40
+ require 'god/condition'
41
+ require 'god/conditions/process_running'
42
+ require 'god/conditions/process_exits'
43
+ require 'god/conditions/tries'
44
+ require 'god/conditions/memory_usage'
45
+ require 'god/conditions/cpu_usage'
46
+ require 'god/conditions/always'
47
+ require 'god/conditions/lambda'
48
+ require 'god/conditions/degrading_lambda'
49
+ require 'god/conditions/flapping'
50
+ require 'god/conditions/http_response_code'
51
+ require 'god/conditions/disk_usage'
52
+ require 'god/conditions/complex'
53
+ require 'god/conditions/file_mtime'
54
+ require 'god/conditions/file_touched'
55
+ require 'god/conditions/socket_responding'
56
+
57
+ require 'god/socket'
58
+ require 'god/driver'
59
+
60
+ require 'god/metric'
61
+ require 'god/watch'
62
+
63
+ require 'god/trigger'
64
+ require 'god/event_handler'
65
+ require 'god/registry'
66
+ require 'god/process'
67
+
68
+ require 'god/cli/version'
69
+ require 'god/cli/command'
70
+
71
+ # ruby 1.8 specific configuration
72
+ if RUBY_VERSION < '1.9'
73
+ $KCODE = 'u'
74
+ end
75
+
76
+ CONTACT_DEPS = { }
77
+ CONTACT_LOAD_SUCCESS = { }
78
+
79
+ def load_contact(name)
80
+ require "god/contacts/#{name}"
81
+ CONTACT_LOAD_SUCCESS[name] = true
82
+ rescue LoadError
83
+ CONTACT_LOAD_SUCCESS[name] = false
84
+ end
85
+
86
+ require 'god/contact'
87
+ load_contact(:campfire)
88
+ load_contact(:hipchat)
89
+ load_contact(:email)
90
+ load_contact(:jabber)
91
+ load_contact(:prowl)
92
+ load_contact(:scout)
93
+ load_contact(:statsd)
94
+ load_contact(:twitter)
95
+ load_contact(:webhook)
96
+ load_contact(:airbrake)
97
+ load_contact(:slack)
98
+ load_contact(:sensu)
99
+
100
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext god])
101
+
102
+ # App wide logging system
103
+ LOG = God::Logger.new
104
+
105
+ def applog(watch, level, text)
106
+ LOG.log(watch, level, text)
107
+ end
108
+
109
+ # The $run global determines whether god should be started when the
110
+ # program would normally end. This should be set to true if when god
111
+ # should be started (e.g. `god -c <config file>`) and false otherwise
112
+ # (e.g. `god status`)
113
+ $run ||= nil
114
+
115
+ GOD_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
116
+
117
+ # Return the binding of god's root level
118
+ def root_binding
119
+ binding
120
+ end
121
+
122
+ module Kernel
123
+ alias_method :abort_orig, :abort
124
+
125
+ def abort(text = nil)
126
+ $run = false
127
+ applog(nil, :error, text) if text
128
+ exit(1)
129
+ end
130
+
131
+ alias_method :exit_orig, :exit
132
+
133
+ def exit(code = 0)
134
+ $run = false
135
+ exit_orig(code)
136
+ end
137
+ end
138
+
139
+ class Module
140
+ def safe_attr_accessor(*args)
141
+ args.each do |arg|
142
+ define_method((arg.to_s + "=").intern) do |other|
143
+ if !self.running && self.inited
144
+ abort "God.#{arg} must be set before any Tasks are defined"
145
+ end
146
+
147
+ if self.running && self.inited
148
+ applog(nil, :warn, "God.#{arg} can't be set while god is running")
149
+ return
150
+ end
151
+
152
+ instance_variable_set(('@' + arg.to_s).intern, other)
153
+ end
154
+
155
+ define_method(arg) do
156
+ instance_variable_get(('@' + arg.to_s).intern)
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ module God
163
+ # The String version number for this package.
164
+ VERSION = '2016.2.20'
165
+
166
+ # The Integer number of lines of backlog to keep for the logger.
167
+ LOG_BUFFER_SIZE_DEFAULT = 100
168
+
169
+ # An Array of directory paths to be used as the default PID file directory.
170
+ # This list will be searched in order and the first one that has write
171
+ # permissions will be used.
172
+ PID_FILE_DIRECTORY_DEFAULTS = ['/var/run/god', '~/.god/pids']
173
+
174
+ # The default Integer port number for the DRb communcations channel.
175
+ DRB_PORT_DEFAULT = 17165
176
+
177
+ # The default Array of String IPs that will allow DRb communication access.
178
+ DRB_ALLOW_DEFAULT = ['127.0.0.1']
179
+
180
+ # The default Symbol log level.
181
+ LOG_LEVEL_DEFAULT = :info
182
+
183
+ # The default Integer number of seconds to wait for god to terminate when
184
+ # issued the quit command.
185
+ TERMINATE_TIMEOUT_DEFAULT = 10
186
+
187
+ # The default Integer number of seconds to wait for a process to terminate.
188
+ STOP_TIMEOUT_DEFAULT = 10
189
+
190
+ # The default String signal to send for the stop command.
191
+ STOP_SIGNAL_DEFAULT = 'TERM'
192
+
193
+ class << self
194
+ # user configurable
195
+ safe_attr_accessor :pid,
196
+ :host,
197
+ :port,
198
+ :allow,
199
+ :log_buffer_size,
200
+ :pid_file_directory,
201
+ :log_file,
202
+ :log_level,
203
+ :use_events,
204
+ :terminate_timeout,
205
+ :socket_user,
206
+ :socket_group,
207
+ :socket_perms
208
+
209
+ # internal
210
+ attr_accessor :inited,
211
+ :running,
212
+ :pending_watches,
213
+ :pending_watch_states,
214
+ :server,
215
+ :watches,
216
+ :groups,
217
+ :contacts,
218
+ :contact_groups,
219
+ :main
220
+ end
221
+
222
+ # Initialize class instance variables.
223
+ self.pid = nil
224
+ self.host = nil
225
+ self.port = nil
226
+ self.allow = nil
227
+ self.log_buffer_size = nil
228
+ self.pid_file_directory = nil
229
+ self.log_level = nil
230
+ self.terminate_timeout = nil
231
+ self.socket_user = nil
232
+ self.socket_group = nil
233
+ self.socket_perms = 0755
234
+
235
+ # Initialize internal data.
236
+ #
237
+ # Returns nothing.
238
+ def self.internal_init
239
+ # Only do this once.
240
+ return if self.inited
241
+
242
+ # Variable init.
243
+ self.watches = {}
244
+ self.groups = {}
245
+ self.pending_watches = []
246
+ self.pending_watch_states = {}
247
+ self.contacts = {}
248
+ self.contact_groups = {}
249
+
250
+ # Set defaults.
251
+ self.log_buffer_size ||= LOG_BUFFER_SIZE_DEFAULT
252
+ self.port ||= DRB_PORT_DEFAULT
253
+ self.allow ||= DRB_ALLOW_DEFAULT
254
+ self.log_level ||= LOG_LEVEL_DEFAULT
255
+ self.terminate_timeout ||= TERMINATE_TIMEOUT_DEFAULT
256
+
257
+ # Additional setup.
258
+ self.setup
259
+
260
+ # Log level.
261
+ log_level_map = {:debug => Logger::DEBUG,
262
+ :info => Logger::INFO,
263
+ :warn => Logger::WARN,
264
+ :error => Logger::ERROR,
265
+ :fatal => Logger::FATAL}
266
+ LOG.level = log_level_map[self.log_level]
267
+
268
+ # Init has been executed.
269
+ self.inited = true
270
+
271
+ # Not yet running.
272
+ self.running = false
273
+ end
274
+
275
+ # Instantiate a new, empty Watch object and pass it to the mandatory block.
276
+ # The attributes of the watch will be set by the configuration file. Aborts
277
+ # on duplicate watch name, invalid watch, or conflicting group name.
278
+ #
279
+ # Returns nothing.
280
+ def self.watch(&block)
281
+ self.task(Watch, &block)
282
+ end
283
+
284
+ # Instantiate a new, empty Task object and yield it to the mandatory block.
285
+ # The attributes of the task will be set by the configuration file. Aborts
286
+ # on duplicate task name, invalid task, or conflicting group name.
287
+ #
288
+ # Returns nothing.
289
+ def self.task(klass = Task)
290
+ # Ensure internal init has run.
291
+ self.internal_init
292
+
293
+ t = klass.new
294
+ yield(t)
295
+
296
+ # Do the post-configuration.
297
+ t.prepare
298
+
299
+ # If running, completely remove the watch (if necessary) to prepare for
300
+ # the reload
301
+ existing_watch = self.watches[t.name]
302
+ if self.running && existing_watch
303
+ self.pending_watch_states[existing_watch.name] = existing_watch.state
304
+ self.unwatch(existing_watch)
305
+ end
306
+
307
+ # Ensure the new watch has a unique name.
308
+ if self.watches[t.name] || self.groups[t.name]
309
+ abort "Task name '#{t.name}' already used for a Task or Group"
310
+ end
311
+
312
+ # Ensure watch is internally valid.
313
+ t.valid? || abort("Task '#{t.name}' is not valid (see above)")
314
+
315
+ # Add to list of watches.
316
+ self.watches[t.name] = t
317
+
318
+ # Add to pending watches.
319
+ self.pending_watches << t
320
+
321
+ # Add to group if specified.
322
+ if t.group
323
+ # Ensure group name hasn't been used for a watch already.
324
+ if self.watches[t.group]
325
+ abort "Group name '#{t.group}' already used for a Task"
326
+ end
327
+
328
+ self.groups[t.group] ||= []
329
+ self.groups[t.group] << t
330
+ end
331
+
332
+ # Register watch.
333
+ t.register!
334
+
335
+ # Log.
336
+ if self.running && existing_watch
337
+ applog(t, :info, "#{t.name} Reloaded config")
338
+ elsif self.running
339
+ applog(t, :info, "#{t.name} Loaded config")
340
+ end
341
+ end
342
+
343
+ # Unmonitor and remove the given watch from god.
344
+ #
345
+ # watch - The Watch to remove.
346
+ #
347
+ # Returns nothing.
348
+ def self.unwatch(watch)
349
+ # Unmonitor.
350
+ watch.unmonitor unless watch.state == :unmonitored
351
+
352
+ # Unregister.
353
+ watch.unregister!
354
+
355
+ # Remove from watches.
356
+ self.watches.delete(watch.name)
357
+
358
+ # Remove from groups.
359
+ if watch.group
360
+ self.groups[watch.group].delete(watch)
361
+ end
362
+
363
+ applog(watch, :info, "#{watch.name} unwatched")
364
+ end
365
+
366
+ # Instantiate a new Contact of the given kind and send it to the block.
367
+ # Then prepare, validate, and record the Contact. Aborts on invalid kind,
368
+ # duplicate contact name, invalid contact, or conflicting group name.
369
+ #
370
+ # kind - The Symbol contact class specifier.
371
+ #
372
+ # Returns nothing.
373
+ def self.contact(kind)
374
+ # Ensure internal init has run.
375
+ self.internal_init
376
+
377
+ # Verify contact has been loaded.
378
+ if CONTACT_LOAD_SUCCESS[kind] == false
379
+ applog(nil, :error, "A required dependency for the #{kind} contact is unavailable.")
380
+ applog(nil, :error, "Run the following commands to install the dependencies:")
381
+ CONTACT_DEPS[kind].each do |d|
382
+ applog(nil, :error, " [sudo] gem install #{d}")
383
+ end
384
+ abort
385
+ end
386
+
387
+ # Create the contact.
388
+ begin
389
+ c = Contact.generate(kind)
390
+ rescue NoSuchContactError => e
391
+ abort e.message
392
+ end
393
+
394
+ # Send to block so config can set attributes.
395
+ yield(c) if block_given?
396
+
397
+ # Call prepare on the contact.
398
+ c.prepare
399
+
400
+ # Remove existing contacts of same name.
401
+ existing_contact = self.contacts[c.name]
402
+ if self.running && existing_contact
403
+ self.uncontact(existing_contact)
404
+ end
405
+
406
+ # Warn and noop if the contact has been defined before.
407
+ if self.contacts[c.name] || self.contact_groups[c.name]
408
+ applog(nil, :warn, "Contact name '#{c.name}' already used for a Contact or Contact Group")
409
+ return
410
+ end
411
+
412
+ # Abort if the Contact is invalid, the Contact will have printed out its
413
+ # own error messages by now.
414
+ unless Contact.valid?(c) && c.valid?
415
+ abort "Exiting on invalid contact"
416
+ end
417
+
418
+ # Add to list of contacts.
419
+ self.contacts[c.name] = c
420
+
421
+ # Add to contact group if specified.
422
+ if c.group
423
+ # Ensure group name hasn't been used for a contact already.
424
+ if self.contacts[c.group]
425
+ abort "Contact Group name '#{c.group}' already used for a Contact"
426
+ end
427
+
428
+ self.contact_groups[c.group] ||= []
429
+ self.contact_groups[c.group] << c
430
+ end
431
+ end
432
+
433
+ # Remove the given contact from god.
434
+ #
435
+ # contact - The Contact to remove.
436
+ #
437
+ # Returns nothing.
438
+ def self.uncontact(contact)
439
+ self.contacts.delete(contact.name)
440
+ if contact.group
441
+ self.contact_groups[contact.group].delete(contact)
442
+ end
443
+ end
444
+
445
+ def self.watches_by_name(name)
446
+ case name
447
+ when "", nil then self.watches.values.dup
448
+ else Array(self.watches[name] || self.groups[name]).dup
449
+ end
450
+ end
451
+
452
+ # Control the lifecycle of the given task(s).
453
+ #
454
+ # name - The String name of a task/group. If empty, invokes command for all tasks.
455
+ # command - The String command to run. Valid commands are:
456
+ # "start", "monitor", "restart", "stop", "unmonitor", "remove".
457
+ #
458
+ # Returns an Array of String task names affected by the command.
459
+ def self.control(name, command)
460
+ # Get the list of items.
461
+ items = self.watches_by_name(name)
462
+
463
+ jobs = []
464
+
465
+ # Do the command.
466
+ case command
467
+ when "start", "monitor"
468
+ items.each { |w| jobs << Thread.new { w.monitor if w.state != :up } }
469
+ when "restart"
470
+ items.each { |w| jobs << Thread.new { w.move(:restart) } }
471
+ when "stop"
472
+ items.each { |w| jobs << Thread.new { w.action(:stop); w.unmonitor if w.state != :unmonitored } }
473
+ when "unmonitor"
474
+ items.each { |w| jobs << Thread.new { w.unmonitor if w.state != :unmonitored } }
475
+ when "remove"
476
+ items.each { |w| self.unwatch(w) }
477
+ else
478
+ raise InvalidCommandError.new
479
+ end
480
+
481
+ jobs.each { |j| j.join }
482
+
483
+ items.map { |x| x.name }
484
+ end
485
+
486
+ # Unmonitor and stop all tasks.
487
+ #
488
+ # Returns true on success, false if all tasks could not be stopped within 10
489
+ # seconds
490
+ def self.stop_all
491
+ self.watches.sort.each do |name, w|
492
+ Thread.new do
493
+ w.action(:stop)
494
+ w.unmonitor if w.state != :unmonitored
495
+ end
496
+ end
497
+
498
+ terminate_timeout.times do
499
+ return true unless self.watches.map { |name, w| w.alive? }.any?
500
+ sleep 1
501
+ end
502
+
503
+ return false
504
+ end
505
+
506
+ # Force the termination of god.
507
+ # * Clean up pid file if one exists
508
+ # * Stop DRb service
509
+ # * Hard exit using exit!
510
+ #
511
+ # Never returns because the process will no longer exist!
512
+ def self.terminate
513
+ FileUtils.rm_f(self.pid) if self.pid
514
+ self.server.stop if self.server
515
+ exit!(0)
516
+ end
517
+
518
+ # Gather the status of each task.
519
+ #
520
+ # Examples
521
+ #
522
+ # God.status
523
+ # # => { 'mongrel' => :up, 'nginx' => :up }
524
+ #
525
+ # Returns a Hash where the key is the String task name and the value is the
526
+ # Symbol status.
527
+ def self.status
528
+ info = {}
529
+ self.watches.map do |name, w|
530
+ info[name] = {:state => w.state, :group => w.group}
531
+ end
532
+ info
533
+ end
534
+
535
+ # Send a signal to each task.
536
+ #
537
+ # name - The String name of the task or group.
538
+ # signal - The String or integer signal to send. e.g. 'HUP', 9.
539
+ #
540
+ # Returns an Array of String names of the tasks affected.
541
+ def self.signal(name, signal)
542
+ items = watches_by_name(name)
543
+ jobs = []
544
+ items.each { |w| jobs << Thread.new { w.signal(signal) } }
545
+ jobs.each { |j| j.join }
546
+ items.map { |x| x.name }
547
+ end
548
+
549
+ # Log lines for the given task since the specified time.
550
+ #
551
+ # watch_name - The String name of the task (may be abbreviated).
552
+ # since - The Time since which to report log lines.
553
+ #
554
+ # Raises God::NoSuchWatchError if no tasks matched.
555
+ # Returns the String of newline separated log lines.
556
+ def self.running_log(watch_name, since)
557
+ matches = pattern_match(watch_name, self.watches.keys)
558
+
559
+ unless matches.first
560
+ raise NoSuchWatchError.new
561
+ end
562
+
563
+ LOG.watch_log_since(matches.first, since)
564
+ end
565
+
566
+ # Load a config file into a running god instance. Rescues any exceptions
567
+ # that the config may raise and reports these back to the caller.
568
+ #
569
+ # code - The String config file contents.
570
+ # filename - The filename of the config file.
571
+ # action - The optional String command specifying how to deal with
572
+ # existing watches. Valid options are: 'stop', 'remove' or
573
+ # 'leave' (default).
574
+ #
575
+ # Returns a three-tuple Array [loaded_names, errors, unloaded_names] where:
576
+ # loaded_names - The Array of String task names that were loaded.
577
+ # errors - The String of error messages produced during the
578
+ # load phase. Will be a blank String if no errors
579
+ # were encountered.
580
+ # unloaded_names - The Array of String task names that were unloaded
581
+ # from the system (if 'remove' or 'stop' was
582
+ # specified as the action).
583
+ def self.running_load(code, filename, action = nil)
584
+ errors = ""
585
+ loaded_watches = []
586
+ unloaded_watches = []
587
+ jobs = []
588
+
589
+ begin
590
+ LOG.start_capture
591
+
592
+ Gem.clear_paths
593
+ eval(code, root_binding, filename)
594
+ self.pending_watches.each do |w|
595
+ if previous_state = self.pending_watch_states[w.name]
596
+ w.monitor unless previous_state == :unmonitored
597
+ else
598
+ w.monitor if w.autostart?
599
+ end
600
+ end
601
+ loaded_watches = self.pending_watches.map { |w| w.name }
602
+ self.pending_watches.clear
603
+ self.pending_watch_states.clear
604
+
605
+ self.watches.each do |name, watch|
606
+ next if loaded_watches.include?(name)
607
+
608
+ case action
609
+ when 'stop'
610
+ jobs << Thread.new(watch) { |w| w.action(:stop); self.unwatch(w) }
611
+ unloaded_watches << name
612
+ when 'remove'
613
+ jobs << Thread.new(watch) { |w| self.unwatch(w) }
614
+ unloaded_watches << name
615
+ when 'leave', '', nil
616
+ # Do nothing
617
+ else
618
+ raise InvalidCommandError, "Unknown action: #{action}"
619
+ end
620
+ end
621
+
622
+ # Make sure we quit capturing when we're done.
623
+ LOG.finish_capture
624
+ rescue Exception => e
625
+ # Don't ever let running_load take down god.
626
+ errors << LOG.finish_capture
627
+
628
+ unless e.instance_of?(SystemExit)
629
+ errors << e.message << "\n"
630
+ errors << e.backtrace.join("\n")
631
+ end
632
+ end
633
+
634
+ jobs.each { |t| t.join }
635
+
636
+ [loaded_watches, errors, unloaded_watches]
637
+ end
638
+
639
+ # Load the given file(s) according to the given glob.
640
+ #
641
+ # glob - The glob-enabled String path to load.
642
+ #
643
+ # Returns nothing.
644
+ def self.load(glob)
645
+ Dir[glob].each do |f|
646
+ Kernel.load f
647
+ end
648
+ end
649
+
650
+ # Setup pid file directory and log system.
651
+ #
652
+ # Returns nothing.
653
+ def self.setup
654
+ if self.pid_file_directory
655
+ # Pid file dir was specified, ensure it is created and writable.
656
+ unless File.exist?(self.pid_file_directory)
657
+ begin
658
+ FileUtils.mkdir_p(self.pid_file_directory)
659
+ rescue Errno::EACCES => e
660
+ abort "Failed to create pid file directory: #{e.message}"
661
+ end
662
+ end
663
+
664
+ unless File.writable?(self.pid_file_directory)
665
+ abort "The pid file directory (#{self.pid_file_directory}) is not writable by #{Etc.getlogin}"
666
+ end
667
+ else
668
+ # No pid file dir specified, try defaults.
669
+ PID_FILE_DIRECTORY_DEFAULTS.each do |idir|
670
+ dir = File.expand_path(idir)
671
+ begin
672
+ FileUtils.mkdir_p(dir)
673
+ if File.writable?(dir)
674
+ self.pid_file_directory = dir
675
+ break
676
+ end
677
+ rescue Errno::EACCES => e
678
+ end
679
+ end
680
+
681
+ unless self.pid_file_directory
682
+ dirs = PID_FILE_DIRECTORY_DEFAULTS.map { |x| File.expand_path(x) }
683
+ abort "No pid file directory exists, could be created, or is writable at any of #{dirs.join(', ')}"
684
+ end
685
+ end
686
+
687
+ if God::Logger.syslog
688
+ LOG.info("Syslog enabled.")
689
+ else
690
+ LOG.info("Syslog disabled.")
691
+ end
692
+
693
+ applog(nil, :info, "Using pid file directory: #{self.pid_file_directory}")
694
+ end
695
+
696
+ # Initialize and startup the machinery that makes god work.
697
+ #
698
+ # Returns nothing.
699
+ def self.start
700
+ self.internal_init
701
+
702
+ # Instantiate server.
703
+ self.server = Socket.new(self.port, self.socket_user, self.socket_group, self.socket_perms)
704
+
705
+ # Start monitoring any watches set to autostart.
706
+ self.watches.values.each { |w| w.monitor if w.autostart? }
707
+
708
+ # Clear pending watches.
709
+ self.pending_watches.clear
710
+
711
+ # Mark as running.
712
+ self.running = true
713
+
714
+ # Don't exit.
715
+ self.main =
716
+ Thread.new do
717
+ loop do
718
+ sleep 60
719
+ end
720
+ end
721
+ end
722
+
723
+ # Prevent god from exiting.
724
+ #
725
+ # Returns nothing.
726
+ def self.join
727
+ self.main.join if self.main
728
+ end
729
+
730
+ # Returns the version String.
731
+ def self.version
732
+ God::VERSION
733
+ end
734
+
735
+ # To be called on program exit to start god.
736
+ #
737
+ # Returns nothing.
738
+ def self.at_exit
739
+ self.start
740
+ self.join
741
+ end
742
+
743
+ # private
744
+
745
+ # Match a shortened pattern against a list of String candidates.
746
+ # The pattern is expanded into a regular expression by
747
+ # inserting .* between each character.
748
+ #
749
+ # pattern - The String containing the abbreviation.
750
+ # list - The Array of Strings to match against.
751
+ #
752
+ # Examples
753
+ #
754
+ # list = %w{ foo bar bars }
755
+ # pattern = 'br'
756
+ # God.pattern_match(list, pattern)
757
+ # # => ['bar', 'bars']
758
+ #
759
+ # Returns the Array of matching name Strings.
760
+ def self.pattern_match(pattern, list)
761
+ regex = pattern.split('').join('.*')
762
+
763
+ list.select do |item|
764
+ item =~ Regexp.new(regex)
765
+ end.sort_by { |x| x.size }
766
+ end
767
+ end
768
+
769
+ # Runs immediately before the program exits. If $run is true,
770
+ # start god, if $run is false, exit normally.
771
+ #
772
+ # Returns nothing.
773
+ at_exit do
774
+ God.at_exit if $run
775
+ end
776
+
777
+ end
778
+
779
+ # provide a new name for the parnt project
780
+ McProc = God