]> git.donarmstrong.com Git - infobot.git/blob - src/Shm.pl
- Manually applied patch from Morten Brix Pedersen. Thanks.
[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;
9 use POSIX qw(_exit);
10
11 sub openSHM {
12     my $IPC_PRIVATE = 0;
13     my $size = 2000;
14
15     if (defined( $_ = shmget($IPC_PRIVATE, $size, 0777) )) {
16         &status("Created shared memory (shm) key: [$_]");
17         return $_;
18     } else {
19         &ERROR("openSHM: failed.");
20         &ERROR("Please delete some shared memory with ipcs or ipcrm.");
21         exit 1;
22     }
23 }
24
25 sub closeSHM {
26     my ($key) = @_;
27     my $IPC_RMID = 0;
28
29     return '' if (!defined $key);
30
31     &shmFlush();
32     &status("Closed shared memory (shm) key: [$key]");
33     return shmctl($key, $IPC_RMID, 0);
34 }
35
36 sub shmRead {
37     my ($key) = @_;
38     my $position = 0;
39     my $size = 3*80;
40     my $retval = '';
41
42     if (shmread($key,$retval,$position,$size)) {
43         return $retval;
44     } else {
45         &ERROR("shmRead: failed: $!");
46         ### TODO: if this fails, never try again.
47         &openSHM();
48         return '';
49     }
50 }
51
52 sub shmWrite {
53     my ($key, $str) = @_;
54     my $position = 0;
55     my $size = 80*3;
56
57     # NULL hack.
58     ### TODO: create shmClear to deal with this.
59     if ($str !~ /^$/) {
60         my $read = &shmRead($key);
61         $read =~ s/\0+//g;
62
63         if ($str eq "") {
64             $str = time().": ";         # time stamping, null.
65         } elsif ($read eq "") {
66             $str = time().": ";         # timestamping.
67         } else {
68             $str = $read ."||". $str;
69         }
70     }
71
72     if (!shmwrite($key,$str,$position,$size)) {
73         &ERROR("shmWrite: failed: $!");
74     }
75 }
76
77 ##############
78 ### Helpers
79 ###
80
81 # Usage: &addForked($name);
82 # Return: 1 for success, 0 for failure.
83 sub addForked {
84     my ($name)          = @_;
85     my $forker_timeout  = 360;  # 6mins, in seconds.
86     $forker             = $name;
87
88     if (!defined $name) {
89         &WARN("addForked: name == NULL.");
90         return 0;
91     }
92
93     foreach (keys %forked) {
94         my $n = $_;
95         my $time = time() - $forked{$n}{Time};
96         next unless ($time > $forker_timeout);
97
98         ### TODO: use &time2string()?
99         &WARN("Fork: looks like we lost '$n', executed $time ago");
100
101         my $pid = $forked{$n}{PID};
102         if (!defined $pid) {
103             &WARN("Fork: no pid for $n.");
104             delete $forked{$n};
105             next;
106         }
107
108         # don't kill parent!
109         if ($pid == $$) {
110             &status("Fork: pid == \$\$ ($$)");
111             next;
112         }
113
114         if ( -d "/proc/$pid") {
115             &status("Fork: killing $name ($pid)");
116             kill 9, $pid;
117         }
118
119         delete $forked{$n};
120     }
121
122     my $count = 0;
123     while (scalar keys %forked > 1) {   # 2 or more == fail.
124         sleep 1;
125
126         if ($count > 3) {       # 3 seconds.
127             my $list = join(', ', keys %forked);
128             if (defined $who) {
129                 &msg($who, "already running ($list) => exceeded allowed forked processes count (1?).");
130             } else {
131                 &status("Fork: I ran too many forked processes :) Giving up $name.");
132             }
133             return 0;
134         }
135
136         $count++;
137     }
138
139     if (exists $forked{$name} and !scalar keys %{ $forked{$name} }) {
140         &WARN("addF: forked{$name} exists but is empty; deleting.");
141         undef $forked{$name};
142     }
143
144     if (exists $forked{$name} and scalar keys %{ $forked{$name} }) {
145         my $time        = $forked{$name}{Time};
146         my $continue    = 0;
147
148         $continue++ if ($forked{$name}{PID} == $$);
149
150         if ($continue) {
151             &WARN("hrm.. fork pid == mypid == $$; how did this happen?");
152
153         } elsif ( -d "/proc/$forked{$name}{PID}") {
154             &status("fork: still running; good. BAIL OUT.");
155             return 0;
156
157         } else {
158             &WARN("Found dead fork; removing and resetting.");
159             $continue = 1;
160         }
161
162         if ($continue) {
163             # NOTHING.
164
165         } elsif (time() - $time > 900) {        # stale fork > 15m.
166             &status("forked: forked{$name} presumably exited without notifying us.");
167
168         } else {                                # fresh fork.
169             &msg($who, "$name is already running ". &Time2String(time() - $time));
170             return 0;
171         }
172     }
173
174     $forked{$name}{Time}        = time();
175     $forked{$name}{PID}         = $$;
176     $forkedtime                 = time();
177     $count{'Fork'}++;
178     return 1;
179 }
180
181 sub delForked {
182     my ($name)  = @_;
183
184     return if ($$ == $bot_pid);
185
186     if (!defined $name) {
187         &WARN("delForked: name == NULL.");
188         POSIX::_exit(0);
189     }
190
191     if ($name =~ /\.pl/) {
192         &WARN("dF: name is name of source file ($name). FIX IT!");
193     }
194
195     &showProc();        # just for informational purposes.
196
197     if (exists $forked{$name}) {
198         my $timestr = &Time2String(time() - $forked{$name}{Time});
199         &status("fork: took $timestr for $name.");
200         &shmWrite($shm,"DELETE FORK $name");
201     } else {
202         &ERROR("delForked: forked{$name} does not exist. should not happen.");
203     }
204
205     &status("--- fork finished for '$name' ---");
206
207     POSIX::_exit(0);
208 }
209
210 1;