]>
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 | ||
105 | if(!anp->nicks && !UHasStaffPriv(vrup) && !UIsCleanupExempt(vrup)) { | |
106 | if(vrup->lastauth && (vrup->lastauth < to_age)) { | |
107 | expired++; | |
108 | cs_log(NULL, "CLEANUPDB inactive user %s %u", vrup->username, vrup->ID); | |
109 | } else if(!vrup->lastauth && (vrup->created < unused_age)) { | |
110 | unauthed++; | |
111 | cs_log(NULL, "CLEANUPDB unused user %s %u", vrup->username, vrup->ID); | |
112 | } else { | |
113 | continue; | |
114 | } | |
115 | ||
116 | cs_removeuser(vrup); | |
117 | } | |
118 | } | |
119 | } | |
120 | ||
121 | cleanuplog("Phase 2 complete, starting phase 3 (chanindex scan)..."); | |
122 | ||
123 | for (i=0;i<CHANNELHASHSIZE;i++) { | |
124 | for (cip=chantable[i];cip;cip=ncip) { | |
125 | ncip=cip->next; | |
126 | if (!(rcp=cip->exts[chanservext])) | |
127 | continue; | |
128 | ||
129 | /* there's a bug here... if no joins or modes are done within the threshold | |
130 | * and someone leaves just before the cleanup then the channel will be nuked. | |
131 | */ | |
132 | ||
133 | /* this is one possible soln but relies on cleanupdb being run more frequently than | |
134 | * the threshold: | |
135 | */ | |
321f10c6 CP |
136 | /* slug: no longer required as we scan the entire network every 1h (cs_hourlyfunc) */ |
137 | /* | |
46542548 CP |
138 | if(cip->channel && cs_ischannelactive(cip->channel, rcp)) { |
139 | rcp->lastactive = t; | |
140 | if (rcp->lastcountersync < (t - COUNTERSYNCINTERVAL)) { | |
141 | csdb_updatechannelcounters(rcp); | |
142 | rcp->lastcountersync=t; | |
143 | } | |
144 | } | |
321f10c6 | 145 | */ |
46542548 CP |
146 | |
147 | if(rcp->lastactive < maxchan_age) { | |
148 | /* don't remove channels with the original founder as an oper */ | |
149 | founder=findreguserbyID(rcp->founder); | |
150 | if(founder && UHasOperPriv(founder)) | |
151 | continue; | |
152 | ||
153 | cs_log(NULL, "CLEANUPDB inactive channel %s", cip->name?cip->name->content:"??"); | |
154 | cs_removechannel(rcp, "Channel deleted due to lack of activity."); | |
155 | chansvaped++; | |
156 | } | |
157 | ||
158 | /* Get rid of any dead chanlev entries */ | |
159 | for (j=0;j<REGCHANUSERHASHSIZE;j++) { | |
160 | for (rcup=rcp->regusers[j];rcup;rcup=nrcup) { | |
161 | nrcup=rcup->nextbychan; | |
162 | ||
163 | if (!rcup->flags) { | |
164 | cs_log(NULL, "Removing user %s from channel %s (no flags)",rcup->user->username,rcp->index->name->content); | |
165 | csdb_deletechanuser(rcup); | |
166 | delreguserfromchannel(rcp, rcup->user); | |
167 | } | |
168 | } | |
169 | } | |
170 | } | |
171 | } | |
172 | ||
173 | cleanuplog("Phase 3 complete, starting phase 4 (history database cleanup) -- runs in the background."); | |
174 | ||
175 | csdb_cleanuphistories(authhistory_age); | |
176 | ||
177 | 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); | |
178 | ||
179 | out: | |
180 | cleanupdb_active=0; | |
181 | } | |
182 | ||
183 | void cs_cleanupdb(nick *np) { | |
184 | cleanupdb(np); | |
185 | } | |
186 | ||
187 | static void cleanupdb(void *arg) { | |
188 | unsigned int to_age; | |
189 | nick *np = (nick *)arg; | |
190 | ||
191 | to_age = time(NULL) - (CLEANUP_ACCOUNT_INACTIVE * 3600 * 24); | |
192 | ||
193 | if(np) { | |
194 | cleanuplog("Manually started by %s.", np->nick); | |
195 | } else { | |
196 | cleanuplog("Automatically started."); | |
197 | } | |
198 | ||
199 | if (cleanupdb_active) { | |
200 | cleanuplog("ABORTED! Cleanup already in progress! BUG BUG BUG!"); | |
201 | return; | |
202 | } | |
203 | ||
204 | cleanuplog("Phase 1 started (auth history data retrieval)..."); | |
205 | ||
206 | /* This query returns a single column containing the userids of all users | |
207 | * who have active sessions now, or sessions which ended in the last | |
208 | * CLEANUP_ACCOUNT_INACTIVE days. */ | |
209 | q9cleanup_asyncquery(cleanupdb_real, NULL, | |
210 | "SELECT userID from chanserv.authhistory WHERE disconnecttime=0 OR disconnecttime > %d GROUP BY userID", to_age); | |
211 | ||
212 | cleanupdb_active=1; | |
213 | } |