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