]> git.donarmstrong.com Git - infobot.git/blob - src/Shm.pl
- berkeley dbm support now works! thanks to tim riker.
[infobot.git] / src / Shm.pl
1 #
2 #   Shm.pl: Shared Memory stuff.
3 #    Author: dms
4 #   Version: 20000201
5 #   Created: 20000124
6 #
7
8 use POSIX qw(_exit);
9
10 sub openSHM {
11     my $IPC_PRIVATE = 0;
12     my $size = 2000;
13
14     if (&IsParam("noSHM")) {
15         &status("Created shared memory: disabled. [bot may become unreliable]");
16         return 0;
17     }
18
19     if (defined( $_ = shmget($IPC_PRIVATE, $size, 0777) )) {
20         &status("Created shared memory (shm) key: [$_]");
21         return $_;
22     } else {
23         &ERROR("openSHM: failed.");
24         &ERROR("Please delete some shared memory with ipcs or ipcrm.");
25         exit 1;
26     }
27 }
28
29 sub closeSHM {
30     my ($key) = @_;
31     my $IPC_RMID = 0;
32
33     return '' if (!defined $key);
34
35     &shmFlush();
36     &status("Closed shared memory (shm) key: [$key]");
37     return shmctl($key, $IPC_RMID, 0);
38 }
39
40 sub shmRead {
41     my ($key) = @_;
42     my $position = 0;
43     my $size = 3*80;
44     my $retval = '';
45
46     return '' if (&IsParam("noSHM"));
47
48     if (shmread($key,$retval,$position,$size)) {
49         return $retval;
50     } else {
51         &ERROR("shmRead: failed: $!");
52         ### TODO: if this fails, never try again.
53         &openSHM();
54         return '';
55     }
56 }
57
58 sub shmWrite {
59     my ($key, $str) = @_;
60     my $position = 0;
61     my $size = 80*3;
62
63     return if (&IsParam("noSHM"));
64
65     # NULL hack.
66     ### TODO: create shmClear to deal with this.
67     if ($str !~ /^$/) {
68         my $read = &shmRead($key);
69         $read =~ s/\0+//g;
70
71         if ($str eq "") {
72             $str = time().": ";         # time stamping, null.
73         } elsif ($read eq "") {
74             $str = time().": ";         # timestamping.
75         } else {
76             $str = $read ."||". $str;
77         }
78     }
79
80     if (!shmwrite($key,$str,$position,$size)) {
81         &ERROR("shmWrite: failed: $!");
82     }
83 }
84
85 ##############
86 ### Helpers
87 ###
88
89 # Usage: &addForked($name);
90 # Return: 1 for success, 0 for failure.
91 sub addForked {
92     my ($name)          = @_;
93     my $forker_timeout  = 360;  # 6mins, in seconds.
94     $forker             = $name;
95
96     if (!defined $name) {
97         &WARN("addForked: name == NULL.");
98         return 0;
99     }
100
101     foreach (keys %forked) {
102         my $n = $_;
103         my $time = time() - $forked{$n}{Time};
104         next unless ($time > $forker_timeout);
105
106         ### TODO: use &time2string()?
107         &WARN("Fork: looks like we lost '$n', executed $time ago");
108
109         my $pid = $forked{$n}{PID};
110         if (!defined $pid) {
111             &WARN("Fork: no pid for $n.");
112             delete $forked{$n};
113             next;
114         }
115
116         # don't kill parent!
117         if ($pid == $$) {
118             &status("Fork: pid == \$\$ ($$)");
119             next;
120         }
121
122         if ( -d "/proc/$pid") {
123             &status("Fork: killing $name ($pid)");
124             kill 9, $pid;
125         }
126
127         delete $forked{$n};
128     }
129
130     my $count = 0;
131     while (scalar keys %forked > 1) {   # 2 or more == fail.
132         sleep 1;
133
134         if ($count > 3) {       # 3 seconds.
135             my $list = join(', ', keys %forked);
136             if (defined $who) {
137                 &msg($who, "already running ($list) => exceeded allowed forked processes count (1?).");
138             } else {
139                 &status("Fork: I ran too many forked processes :) Giving up $name.");
140             }
141             return 0;
142         }
143
144         $count++;
145     }
146
147     if (exists $forked{$name} and !scalar keys %{ $forked{$name} }) {
148         &WARN("addF: forked{$name} exists but is empty; deleting.");
149         undef $forked{$name};
150     }
151
152     if (exists $forked{$name} and scalar keys %{ $forked{$name} }) {
153         my $time        = $forked{$name}{Time};
154         my $continue    = 0;
155
156         $continue++ if ($forked{$name}{PID} == $$);
157
158         if ($continue) {
159             &WARN("hrm.. fork pid == mypid == $$; how did this happen?");
160
161         } elsif ( -d "/proc/$forked{$name}{PID}") {
162             &status("fork: still running; good. BAIL OUT.");
163             return 0;
164
165         } else {
166             &WARN("Found dead fork; removing and resetting.");
167             $continue = 1;
168         }
169
170         if ($continue) {
171             # NOTHING.
172
173         } elsif (time() - $time > 900) {        # stale fork > 15m.
174             &status("forked: forked{$name} presumably exited without notifying us.");
175
176         } else {                                # fresh fork.
177             &msg($who, "$name is already running ". &Time2String(time() - $time));
178             return 0;
179         }
180     }
181
182     $forked{$name}{Time}        = time();
183     $forked{$name}{PID}         = $$;
184     $forkedtime                 = time();
185     $count{'Fork'}++;
186     return 1;
187 }
188
189 sub delForked {
190     my ($name)  = @_;
191
192     return if ($$ == $bot_pid);
193
194     if (!defined $name) {
195         &WARN("delForked: name == NULL.");
196         POSIX::_exit(0);
197     }
198
199     if ($name =~ /\.pl/) {
200         &WARN("dF: name is name of source file ($name). FIX IT!");
201     }
202
203     &showProc();        # just for informational purposes.
204
205     if (exists $forked{$name}) {
206         my $timestr = &Time2String(time() - $forked{$name}{Time});
207         &status("fork: took $timestr for $name.");
208         &shmWrite($shm,"DELETE FORK $name");
209     } else {
210         &ERROR("delForked: forked{$name} does not exist. should not happen.");
211     }
212
213     &status("--- fork finished for '$name' ---");
214
215     POSIX::_exit(0);
216 }
217
218 sub shmFlush {
219     return if ($$ != $::bot_pid); # fork protection.
220
221     if (@_) {
222         &ScheduleThis(5, "shmFlush");
223         return if ($_[0] eq "2");
224     }
225
226     my $time;
227     my $shmmsg = &shmRead($shm);
228     $shmmsg =~ s/\0//g;         # remove padded \0's.
229     if ($shmmsg =~ s/^(\d+): //) {
230         $time   = $1;
231     }
232
233     foreach (split '\|\|', $shmmsg) {
234         next if (/^$/);
235         &VERB("shm: Processing '$_'.",2);
236
237         if (/^DCC SEND (\S+) (\S+)$/) {
238             my ($nick,$file) = ($1,$2);
239             if (exists $dcc{'SEND'}{$who}) {
240                 &msg($nick, "DCC already active.");
241             } else {
242                 &DEBUG("shm: dcc sending $2 to $1.");
243                 $conn->new_send($1,$2);
244                 $dcc{'SEND'}{$who} = time();
245             }
246         } elsif (/^SET FORKPID (\S+) (\S+)/) {
247             $forked{$1}{PID} = $2;
248         } elsif (/^DELETE FORK (\S+)$/) {
249             delete $forked{$1};
250         } elsif (/^EVAL (.*)$/) {
251             &DEBUG("evaling '$1'.");
252             eval $1;
253         } else {
254             &DEBUG("shm: unknown msg. ($_)");
255         }
256     }
257
258     &shmWrite($shm,"") if ($shmmsg ne "");
259 }
260
261 1;