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