]>
Commit | Line | Data |
---|---|---|
46542548 CP |
1 | #include "chanserv.h" |
2 | #include "../lib/irc_string.h" | |
3 | #include <stdio.h> | |
4 | #include <string.h> | |
5 | ||
6 | static void cleanupdb(void *arg); | |
7 | static void schedulecleanup(int hooknum, void *arg); | |
8 | ||
9 | static unsigned int cleanupdb_active; | |
10 | static DBModuleIdentifier q9cleanupdbid; | |
11 | ||
12 | void _init() { | |
13 | q9cleanupdbid = dbgetid(); | |
14 | ||
15 | registerhook(HOOK_CHANSERV_DBLOADED, schedulecleanup); | |
16 | ||
17 | if (chanservdb_ready) | |
18 | schedulecleanup(HOOK_CHANSERV_DBLOADED, NULL); | |
19 | } | |
20 | ||
21 | void _fini() { | |
22 | deleteallschedules(cleanupdb); | |
23 | dbfreeid(q9cleanupdbid); | |
24 | } | |
25 | ||
26 | static void schedulecleanup(int hooknum, void *arg) { | |
321f10c6 | 27 | /* run at 1am but only if we're more than 15m away from it, otherwise run tomorrow */ |
46542548 CP |
28 | |
29 | time_t t = time(NULL); | |
321f10c6 CP |
30 | time_t next_run = ((t / 86400) * 86400 + 86400) + 3600; |
31 | if(next_run - t < 900) | |
32 | next_run+=86400; | |
33 | ||
34 | schedulerecurring(next_run,0,86400,cleanupdb,NULL); | |
46542548 CP |
35 | } |
36 | ||
37 | __attribute__ ((format (printf, 1, 2))) | |
38 | static void cleanuplog(char *format, ...) { | |
39 | char buf[512]; | |
40 | va_list va; | |
41 | ||
42 | va_start(va, format); | |
43 | vsnprintf(buf, sizeof(buf), format, va); | |
44 | va_end(va); | |
45 | ||
46 | cs_log(NULL, "CLEANUPDB %s", buf); | |
47 | chanservwallmessage("CLEANUPDB: %s", buf); | |
48 | } | |
49 | ||
50 | static void cleanupdb_real(DBConn *dbconn, void *arg) { | |
51 | reguser *vrup, *srup, *founder; | |
52 | regchanuser *rcup, *nrcup; | |
53 | authname *anp; | |
54 | int i,j; | |
55 | time_t t, to_age, unused_age, maxchan_age, authhistory_age; | |
56 | int expired = 0, unauthed = 0, chansvaped = 0; | |
57 | chanindex *cip, *ncip; | |
58 | regchan *rcp; | |
59 | DBResult *pgres; | |
60 | unsigned int themarker; | |
61 | unsigned int id; | |
62 | ||
63 | t = time(NULL); | |
64 | to_age = t - (CLEANUP_ACCOUNT_INACTIVE * 3600 * 24); | |
65 | unused_age = t - (CLEANUP_ACCOUNT_UNUSED * 3600 * 24); | |
66 | maxchan_age = t - (CLEANUP_CHANNEL_INACTIVE * 3600 * 24); | |
67 | authhistory_age = t - (CLEANUP_AUTHHISTORY * 3600 * 24); | |
68 | ||
69 | themarker=nextauthnamemarker(); | |
70 | ||
71 | if (!dbconn) { | |
72 | cleanuplog("No DB connection, aborting."); | |
73 | goto out; | |
74 | } | |
75 | ||
76 | pgres=dbgetresult(dbconn); | |
77 | ||
78 | if (!dbquerysuccessful(pgres)) { | |
79 | cleanuplog("DB error, aborting."); | |
80 | goto out; | |
81 | } | |
82 | ||
83 | while (dbfetchrow(pgres)) { | |
84 | id=strtoul(dbgetvalue(pgres, 0), NULL, 10); | |
85 | anp=findauthname(id); | |
86 | if (anp) | |
87 | anp->marker=themarker; | |
88 | } | |
89 | ||
90 | dbclear(pgres); | |
91 | ||
92 | cleanuplog("Phase 1 complete, starting phase 2 (regusers scan)..."); | |
93 | ||
94 | for (i=0;i<REGUSERHASHSIZE;i++) { | |
95 | for (vrup=regusernicktable[i]; vrup; vrup=srup) { | |
96 | srup=vrup->nextbyname; | |
97 | if (!(anp=findauthname(vrup->ID))) | |
98 | continue; /* should maybe raise hell instead */ | |
99 | ||
100 | /* If this user has the right marker, this means the authtracker data | |
101 | * indicates that they have been active recently */ | |
102 | if (anp->marker == themarker) | |
103 | continue; | |
104 | ||
9172b03e CP |
105 | /* HACK: don't ever delete the last user -- prevents userids being reused */ |
106 | if (vrup->ID == lastuserID) | |
107 | continue; | |
108 | ||
46542548 CP |
109 | if(!anp->nicks && !UHasStaffPriv(vrup) && !UIsCleanupExempt(vrup)) { |
110 | if(vrup->lastauth && (vrup->lastauth < to_age)) { | |
111 | expired++; | |
112 | cs_log(NULL, "CLEANUPDB inactive user %s %u", vrup->username, vrup->ID); | |
113 | } else if(!vrup->lastauth && (vrup->created < unused_age)) { | |
114 | unauthed++; | |
115 | cs_log(NULL, "CLEANUPDB unused user %s %u", vrup->username, vrup->ID); | |
116 | } else { | |
117 | continue; | |
118 | } | |
119 | ||
120 | cs_removeuser(vrup); | |
121 | } | |
122 | } | |
123 | } | |
124 | ||
125 | cleanuplog("Phase 2 complete, starting phase 3 (chanindex scan)..."); | |
126 | ||
127 | for (i=0;i<CHANNELHASHSIZE;i++) { | |
128 | for (cip=chantable[i];cip;cip=ncip) { | |
129 | ncip=cip->next; | |
130 | if (!(rcp=cip->exts[chanservext])) | |
131 | continue; | |
132 | ||
9172b03e CP |
133 | /* HACK: don't ever delete the last channel -- prevents channelids being reused */ |
134 | if (rcp->ID == lastchannelID) | |
135 | continue; | |
136 | ||
46542548 CP |
137 | /* there's a bug here... if no joins or modes are done within the threshold |
138 | * and someone leaves just before the cleanup then the channel will be nuked. | |
139 | */ | |
140 | ||
141 | /* this is one possible soln but relies on cleanupdb being run more frequently than | |
142 | * the threshold: | |
143 | */ | |
321f10c6 CP |
144 | /* slug: no longer required as we scan the entire network every 1h (cs_hourlyfunc) */ |
145 | /* | |
46542548 CP |
146 | if(cip->channel && cs_ischannelactive(cip->channel, rcp)) { |
147 | rcp->lastactive = t; | |
148 | if (rcp->lastcountersync < (t - COUNTERSYNCINTERVAL)) { | |
149 | csdb_updatechannelcounters(rcp); | |
150 | rcp->lastcountersync=t; | |
151 | } | |
152 | } | |
321f10c6 | 153 | */ |
46542548 CP |
154 | |
155 | if(rcp->lastactive < maxchan_age) { | |
156 | /* don't remove channels with the original founder as an oper */ | |
157 | founder=findreguserbyID(rcp->founder); | |
158 | if(founder && UHasOperPriv(founder)) | |
159 | continue; | |
160 | ||
161 | cs_log(NULL, "CLEANUPDB inactive channel %s", cip->name?cip->name->content:"??"); | |
162 | cs_removechannel(rcp, "Channel deleted due to lack of activity."); | |
163 | chansvaped++; | |
164 | } | |
165 | ||
166 | /* Get rid of any dead chanlev entries */ | |
167 | for (j=0;j<REGCHANUSERHASHSIZE;j++) { | |
168 | for (rcup=rcp->regusers[j];rcup;rcup=nrcup) { | |
169 | nrcup=rcup->nextbychan; | |
170 | ||
171 | if (!rcup->flags) { | |
172 | cs_log(NULL, "Removing user %s from channel %s (no flags)",rcup->user->username,rcp->index->name->content); | |
173 | csdb_deletechanuser(rcup); | |
174 | delreguserfromchannel(rcp, rcup->user); | |
175 | } | |
176 | } | |
177 | } | |
178 | } | |
179 | } | |
180 | ||
181 | cleanuplog("Phase 3 complete, starting phase 4 (history database cleanup) -- runs in the background."); | |
182 | ||
183 | csdb_cleanuphistories(authhistory_age); | |
184 | ||
185 | cleanuplog("Stats: %d accounts inactive for %d days, %d accounts weren't used within %d days, %d channels were inactive for %d days.", expired, CLEANUP_ACCOUNT_INACTIVE, unauthed, CLEANUP_ACCOUNT_UNUSED, chansvaped, CLEANUP_CHANNEL_INACTIVE); | |
186 | ||
187 | out: | |
188 | cleanupdb_active=0; | |
189 | } | |
190 | ||
191 | void cs_cleanupdb(nick *np) { | |
192 | cleanupdb(np); | |
193 | } | |
194 | ||
195 | static void cleanupdb(void *arg) { | |
196 | unsigned int to_age; | |
197 | nick *np = (nick *)arg; | |
198 | ||
199 | to_age = time(NULL) - (CLEANUP_ACCOUNT_INACTIVE * 3600 * 24); | |
200 | ||
201 | if(np) { | |
202 | cleanuplog("Manually started by %s.", np->nick); | |
203 | } else { | |
204 | cleanuplog("Automatically started."); | |
205 | } | |
206 | ||
207 | if (cleanupdb_active) { | |
208 | cleanuplog("ABORTED! Cleanup already in progress! BUG BUG BUG!"); | |
209 | return; | |
210 | } | |
211 | ||
212 | cleanuplog("Phase 1 started (auth history data retrieval)..."); | |
213 | ||
214 | /* This query returns a single column containing the userids of all users | |
215 | * who have active sessions now, or sessions which ended in the last | |
216 | * CLEANUP_ACCOUNT_INACTIVE days. */ | |
1a17d1db CP |
217 | |
218 | dbquery("BEGIN TRANSACTION;"); | |
219 | ||
220 | /* increase memory for aggregate (GROUP BY) -- query can take hours if this spills to disk */ | |
221 | dbquery("SET LOCAL work_mem = '512MB';"); | |
46542548 | 222 | q9cleanup_asyncquery(cleanupdb_real, NULL, |
1a17d1db CP |
223 | "SELECT userID from chanserv.authhistory WHERE disconnecttime=0 OR disconnecttime > %d GROUP BY userID;", to_age); |
224 | dbquery("COMMIT;"); | |
225 | ||
46542548 CP |
226 | cleanupdb_active=1; |
227 | } |