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