2 #include "../lib/irc_string.h"
6 static void cleanupdb(void *arg
);
7 static void schedulecleanup(int hooknum
, void *arg
);
9 static unsigned int cleanupdb_active
;
10 static DBModuleIdentifier q9cleanupdbid
;
13 q9cleanupdbid
= dbgetid();
15 registerhook(HOOK_CHANSERV_DBLOADED
, schedulecleanup
);
18 schedulecleanup(HOOK_CHANSERV_DBLOADED
, NULL
);
22 deleteallschedules(cleanupdb
);
23 dbfreeid(q9cleanupdbid
);
26 static void schedulecleanup(int hooknum
, void *arg
) {
27 /* run at 1am but only if we're more than 15m away from it, otherwise run tomorrow */
29 time_t t
= time(NULL
);
30 time_t next_run
= ((t
/ 86400) * 86400 + 86400) + 3600;
31 if(next_run
- t
< 900)
34 schedulerecurring(next_run
,0,86400,cleanupdb
,NULL
);
37 __attribute__ ((format (printf
, 1, 2)))
38 static void cleanuplog(char *format
, ...) {
43 vsnprintf(buf
, sizeof(buf
), format
, va
);
46 cs_log(NULL
, "CLEANUPDB %s", buf
);
47 chanservwallmessage("CLEANUPDB: %s", buf
);
50 static void cleanupdb_real(DBConn
*dbconn
, void *arg
) {
51 reguser
*vrup
, *srup
, *founder
;
52 regchanuser
*rcup
, *nrcup
;
55 time_t t
, to_age
, unused_age
, maxchan_age
, authhistory_age
;
56 int expired
= 0, unauthed
= 0, chansvaped
= 0;
57 chanindex
*cip
, *ncip
;
60 unsigned int themarker
;
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);
69 themarker
=nextauthnamemarker();
72 cleanuplog("No DB connection, aborting.");
76 pgres
=dbgetresult(dbconn
);
78 if (!dbquerysuccessful(pgres
)) {
79 cleanuplog("DB error, aborting.");
83 while (dbfetchrow(pgres
)) {
84 id
=strtoul(dbgetvalue(pgres
, 0), NULL
, 10);
87 anp
->marker
=themarker
;
92 cleanuplog("Phase 1 complete, starting phase 2 (regusers scan)...");
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 */
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
)
105 /* HACK: don't ever delete the last user -- prevents userids being reused */
106 if (vrup
->ID
== lastuserID
)
109 if(!anp
->nicks
&& !UHasStaffPriv(vrup
) && !UIsCleanupExempt(vrup
)) {
110 if(vrup
->lastauth
&& (vrup
->lastauth
< to_age
)) {
112 cs_log(NULL
, "CLEANUPDB inactive user %s %u", vrup
->username
, vrup
->ID
);
113 } else if(!vrup
->lastauth
&& (vrup
->created
< unused_age
)) {
115 cs_log(NULL
, "CLEANUPDB unused user %s %u", vrup
->username
, vrup
->ID
);
125 cleanuplog("Phase 2 complete, starting phase 3 (chanindex scan)...");
127 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
128 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
130 if (!(rcp
=cip
->exts
[chanservext
]))
133 /* HACK: don't ever delete the last channel -- prevents channelids being reused */
134 if (rcp
->ID
== lastchannelID
)
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.
141 /* this is one possible soln but relies on cleanupdb being run more frequently than
144 /* slug: no longer required as we scan the entire network every 1h (cs_hourlyfunc) */
146 if(cip->channel && cs_ischannelactive(cip->channel, rcp)) {
148 if (rcp->lastcountersync < (t - COUNTERSYNCINTERVAL)) {
149 csdb_updatechannelcounters(rcp);
150 rcp->lastcountersync=t;
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
))
161 cs_log(NULL
, "CLEANUPDB inactive channel %s", cip
->name
?cip
->name
->content
:"??");
162 cs_removechannel(rcp
, "Channel deleted due to lack of activity.");
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
;
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
);
181 cleanuplog("Phase 3 complete, starting phase 4 (history database cleanup) -- runs in the background.");
183 csdb_cleanuphistories(authhistory_age
);
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
);
191 void cs_cleanupdb(nick
*np
) {
195 static void cleanupdb(void *arg
) {
197 nick
*np
= (nick
*)arg
;
199 to_age
= time(NULL
) - (CLEANUP_ACCOUNT_INACTIVE
* 3600 * 24);
202 cleanuplog("Manually started by %s.", np
->nick
);
204 cleanuplog("Automatically started.");
207 if (cleanupdb_active
) {
208 cleanuplog("ABORTED! Cleanup already in progress! BUG BUG BUG!");
212 cleanuplog("Phase 1 started (auth history data retrieval)...");
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. */
218 dbquery("BEGIN TRANSACTION;");
220 /* increase memory for aggregate (GROUP BY) -- query can take hours if this spills to disk */
221 dbquery("SET LOCAL work_mem = '512MB';");
222 q9cleanup_asyncquery(cleanupdb_real
, NULL
,
223 "SELECT userID from chanserv.authhistory WHERE disconnecttime=0 OR disconnecttime > %d GROUP BY userID;", to_age
);