]> code.citadel.org Git - citadel.git/blob - citadel/server/housekeeping.c
4f8b1a9d99596304e758f3436e685f9701ad4bce
[citadel.git] / citadel / server / housekeeping.c
1 // This file contains miscellaneous housekeeping tasks.
2 //
3 // Copyright (c) 1987-2021 by the citadel.org team
4 //
5 // This program is open source software.  Use, duplication, or disclosure
6 // is subject to the terms of the GNU General Public License, version 3.
7 // The program is distributed without any warranty, expressed or implied.
8
9
10 #include <stdio.h>
11 #include <libcitadel.h>
12
13 #include "ctdl_module.h"
14 #include "serv_extensions.h"
15 #include "room_ops.h"
16 #include "internet_addressing.h"
17 #include "config.h"
18 #include "journaling.h"
19 #include "citadel_ldap.h"
20
21 void check_sched_shutdown(void) {
22         if ((ScheduledShutdown == 1) && (ContextList == NULL)) {
23                 syslog(LOG_NOTICE, "housekeeping: scheduled shutdown initiating");
24                 server_shutting_down = 1;
25         }
26 }
27
28
29 // Check (and fix) floor reference counts.  This doesn't need to be done
30 // very often, since the counts should remain correct during normal operation.
31 void check_ref_counts_backend(struct ctdlroom *qrbuf, void *data) {
32
33         int *new_refcounts;
34
35         new_refcounts = (int *) data;
36
37         ++new_refcounts[(int)qrbuf->QRfloor];
38 }
39
40
41 void check_ref_counts(void) {
42         struct floor flbuf;
43         int a;
44
45         int new_refcounts[MAXFLOORS];
46
47         syslog(LOG_DEBUG, "housekeeping: checking floor reference counts");
48         for (a=0; a<MAXFLOORS; ++a) {
49                 new_refcounts[a] = 0;
50         }
51
52         cdb_begin_transaction();
53         CtdlForEachRoom(check_ref_counts_backend, (void *)new_refcounts );
54         cdb_end_transaction();
55
56         for (a=0; a<MAXFLOORS; ++a) {
57                 lgetfloor(&flbuf, a);
58                 flbuf.f_ref_count = new_refcounts[a];
59                 if (new_refcounts[a] > 0) {
60                         flbuf.f_flags = flbuf.f_flags | QR_INUSE;
61                 }
62                 else {
63                         flbuf.f_flags = flbuf.f_flags & ~QR_INUSE;
64                 }
65                 lputfloor(&flbuf, a);
66                 syslog(LOG_DEBUG, "housekeeping: floor %d has %d rooms", a, new_refcounts[a]);
67         }
68 }
69
70
71 // Provide hints as to whether we have any memory leaks
72 void keep_an_eye_on_memory_usage(void) {
73         static void *original_brk = NULL;
74         if (!original_brk) original_brk = sbrk(0);      // Remember the original program break so we can test for leaks
75         syslog(LOG_DEBUG, "original_brk=%lx, current_brk=%lx, addl=%ld", (long)original_brk, (long)sbrk(0), (long)(sbrk(0)-original_brk));      // FIXME not so noisy please
76 }
77
78
79 // This is the housekeeping loop.  Worker threads come through here after
80 // processing client requests but before jumping back into the pool.  We
81 // only allow housekeeping to execute once per minute, and we only allow one
82 // instance to run at a time.
83 static int housekeeping_in_progress = 0;
84 static int housekeeping_disabled = 0;
85 static time_t last_timer = 0L;
86
87 void do_housekeeping(void) {
88         int do_housekeeping_now = 0;
89         int do_perminute_housekeeping_now = 0;
90         time_t now;
91
92         if (housekeeping_disabled) {
93                 return;
94         }
95
96         // We do it this way instead of wrapping the whole loop in an
97         // S_HOUSEKEEPING critical section because it eliminates the need to
98         // potentially have multiple concurrent mutexes in progress.
99         begin_critical_section(S_HOUSEKEEPING);
100         if (housekeeping_in_progress == 0) {
101                 do_housekeeping_now = 1;
102                 housekeeping_in_progress = 1;
103         }
104         end_critical_section(S_HOUSEKEEPING);
105
106         now = time(NULL);
107         if ( (do_housekeeping_now == 0) && (!CtdlIsSingleUser()) ) {
108                 if ( (now - last_timer) > (time_t)300 ) {
109                         syslog(LOG_WARNING,
110                                 "housekeeping: WARNING: housekeeping loop has not run for %ld minutes.  Is something stuck?",
111                                 ((now - last_timer) / 60)
112                         );
113                 }
114                 return;
115         }
116
117         // Ok, at this point we've made the decision to run the housekeeping
118         // loop.  Everything below this point is real work.
119
120         if ( (now - last_timer) > (time_t)60 ) {
121                 do_perminute_housekeeping_now = 1;
122                 last_timer = time(NULL);
123         }
124
125         // First, do the "as often as needed" stuff...
126         JournalRunQueue();
127         PerformSessionHooks(EVT_HOUSE);
128
129         // Then, do the "once per minute" stuff...
130         if (do_perminute_housekeeping_now) {
131                 cdb_check_handles();
132                 PerformSessionHooks(EVT_TIMER);         // Run all registered TIMER hooks
133
134                 // LDAP sync isn't in a module so we can put it here
135                 static time_t last_ldap_sync = 0L;
136                 if ( (now - last_ldap_sync) > (time_t)CtdlGetConfigLong("c_ldap_sync_freq") ) {
137                         CtdlSynchronizeUsersFromLDAP();
138                         last_ldap_sync = time(NULL);
139                 }
140
141         keep_an_eye_on_memory_usage();
142         }
143
144         // All done.
145         begin_critical_section(S_HOUSEKEEPING);
146         housekeeping_in_progress = 0;
147         end_critical_section(S_HOUSEKEEPING);
148 }
149
150
151 void CtdlDisableHouseKeeping(void) {
152         syslog(LOG_INFO, "housekeeping: trying to disable");
153         while ( (!housekeeping_disabled) && (!server_shutting_down) && (!housekeeping_in_progress) ) {
154
155                 if (housekeeping_in_progress) {
156                         sleep(1);
157                 }
158                 else {
159                         begin_critical_section(S_HOUSEKEEPING);
160                         if (!housekeeping_in_progress) {
161                                 housekeeping_disabled = 1;
162                         }
163                         end_critical_section(S_HOUSEKEEPING);
164                 }
165         }
166         syslog(LOG_INFO, "housekeeping: disabled now");
167 }
168
169
170 void CtdlEnableHouseKeeping(void) {
171         begin_critical_section(S_HOUSEKEEPING);
172         housekeeping_in_progress = 0;
173         end_critical_section(S_HOUSEKEEPING);
174 }